在 DOM 中动态插入并执行脚本

spyc1688 8年前
   <p>在 HTML 中脚本以 <script> 来标记,通过设置其内容或 src 属性执行内联脚本或外部脚本。 本文讨论动态地插入脚本标签时浏览器对它的解析、下载和执行行为。 动态插入脚本的场景可能包括使用 AJAX 获取脚本并动态执行(多用于性能优化), 以及运行时决定执行页面模板中的某段脚本(多用于单页异步)。</p>    <p>动态执行脚本还有其他方式,比如 eval 和 new Function ,这些不在本文的讨论范围。</p>    <h2>执行内联脚本</h2>    <p>为了插入内联脚本,可以创建一个 script 元素并设置其内容,插入到 DOM 即可立即执行。 例如:</p>    <pre>  <code class="language-javascript">var script = document.createElement('script');  script.text = 'console.log("foo")';  document.body.appendChild(script);</code></pre>    <p>以下写法是等价的</p>    <pre>  <code class="language-javascript">script.text = 'console.log("foo")';  script.innerText = 'console.log("foo")';  script.innerHTML = 'console.log("foo")';</code></pre>    <p>需要注意的是内联脚本是否能够执行仍然受制于 CSP策略指令 , 该策略是由 Content-Security-Policy 响应头( rfc7762 )控制的。 例如下列设置将会禁止执行 harttle.com 以外的任何内联脚本。</p>    <pre>  <code class="language-javascript">Content-Security-Policy: script-src harttle.com;</code></pre>    <h2>执行外部脚本</h2>    <p>插入并执行外部脚本的方法与内联脚本类似,只需设置 script.src 属性并插入到 DOM。 例如:</p>    <pre>  <code class="language-javascript">var script = document.createElement('script');  script.src = 'foo.js';  document.body.appendChild(script);</code></pre>    <p>与内联脚本不同的是,外部脚本的插入是异步的不会阻塞 DOM 解析。</p>    <p>此外有一个细节可能需要注意:一旦设置了 src 属性, <script> 标签本身的所有内容就不会再被执行了。</p>    <h2>innerHTML</h2>    <p>innerHTML 属性可用来设置 DOM 内容,但不可用来插入并执行 <script> 。 下面的内联脚本和外部脚本都不会被执行:</p>    <pre>  <code class="language-javascript">document.body.innerHTML = '<script src="foo.js"></script>'  document.body.innerHTML = '<script>console.log("foo")</script>'</code></pre>    <p>在设置 innerHTML 时,浏览器会初始化一个新的 HTML Parser 来解析它。 只要与该 Parser 关联的 DOM 启用了 JavaScript(通常是启用的),脚本的 scripting flag 就为真, 但是即便如此, HTML 片段的解析过程中,脚本是不会执行的 。</p>    <p>Create a new HTML parser, and associate it with the just created Document node. – 12.4 Parsing HTML fragments , WHATWG</p>    <p>The scripting flag can be enabled even when the parser was originally created for the HTML fragment parsing algorithm, even though script elements don’t execute in that case. – 12.2.3.5 Other parsing state flags , WHATWG</p>    <p>事实上,设置 innerHTML 和 outerHTML 都不执行脚本,但 document.write() 是会同步执行的。</p>    <p>When inserted using the document.write() method, script elements execute (typically blocking further script execution or HTML parsing), but when inserted using innerHTML and outerHTML attributes, they do not execute at all. – 4.12.1 The script element WHATWG</p>    <h2>jQuery DOM Eval</h2>    <p>我们知道使用 jQuery html() 方法时插入的脚本总是执行的,jQuery 会检查传入的内容,并执行其中的每一个脚本。 源码在 <a href="/misc/goto?guid=4959734852046499063" rel="nofollow,noindex">src/core/DOMEval.js</a> :</p>    <pre>  <code class="language-javascript">function DOMEval( code, doc ) {      doc = doc || document;      var script = doc.createElement( "script" );      script.text = code;      doc.head.appendChild( script ).parentNode.removeChild( script );  }</code></pre>    <h2> </h2>    <p>来自:http://harttle.com/2017/01/16/dynamic-script-insertion.html</p>    <p> </p>