[转]Script标签和脚本执行顺序

Script标签和脚本执行顺序

这里详细聊聊和script标签相关的脚本执行顺序。html

Script标签的默认行为

几个首要特性:浏览器

  • script标签(不带deferasync属性)的会阻止文档渲染。相关脚本会当即下载并执行。
  • document.currentScript能够得到当前正在运行的脚本(Chrome 29+, FF4+)
  • 脚本顺序再默认状况下和script标签出现的顺序一致

假设以下简单代码1,最终会产生三个alert依次为“A”、“B”、“C”。网络

<!-- HTML Code -->
<script>alert("A");</script>
<script>alert("B");</script>
<script>alert("C");</script>

咱们再考虑有网络请求的状况2app

<!-- HTML code -->
<script  src="https://snipt.net/raw/7b08744009c450e07c0bfc1d606fc72e/"></script>
<script  src="https://snipt.net/raw/a2e8c05c1f6fc0e47d259aa899304e89/"></script>
<script  src="https://snipt.net/raw/4fab3017d3d46cbfc4bbd88aab006650/"></script>

三个文件都须要先下载再运行,且第二个文件的尺寸远大于另外两个文件。但结果依然是弹出三个alert,内容分别是”A”、”B”、”C”。异步

从上面两个例子,能够充分了解到script标签的柱塞式执行。async

async属性

async属性是HTML5的新特性3,这意味着其兼容性并不乐观(IE10+)。fetch

async表示该script标签并不柱塞,也不一样步执行。浏览器只须要在脚本下载完毕后再执行便可——没必要柱塞页面渲染等待该脚本的下载和执行。.net

以下代码4,会获得三个alert,可是alert的内容分别是”A”,”C”,”B”。3d

<!-- HTML code -->
<script  src="https://snipt.net/raw/7b08744009c450e07c0bfc1d606fc72e/"></script>
<script  src="https://snipt.net/raw/a2e8c05c1f6fc0e47d259aa899304e89/" async=true></script>
<script  src="https://snipt.net/raw/4fab3017d3d46cbfc4bbd88aab006650/"></script>

能够看到,第二个script标签在加入async并无阻止后续文档解析和脚本执行。code

考究这个属性产生的原有,其实有大量的脚本加载器在作这样的事情:

var script = document.createElement("script");
script.src = "file.js";
document.body.appendChild(script);

不难想象,经过脚本异步插入的script标签达到的效果和带async属性的script标签是同样的。换句话说,由脚本插入的script标签默认是async的。

另外,对內联脚本设置async属性是没有意义的,也不产生其余效果。其包含的脚本老是当即执行的。

defer属性

带有defer属性的脚本,一样会推迟脚本的执行,而且不会阻止文档解析。就如同这个脚本,放置到了文档的末尾(</body>以前)。

以下代码5的宏观现象和加了async属性的例子是同样的,都会获得”A”、”C”、”B”的三个alert。可是其原理是不同的。

<!-- HTML code -->
<script  src="https://snipt.net/raw/7b08744009c450e07c0bfc1d606fc72e/"></script>
<script  src="https://snipt.net/raw/a2e8c05c1f6fc0e47d259aa899304e89/" defer=true></script>
<script  src="https://snipt.net/raw/4fab3017d3d46cbfc4bbd88aab006650/"></script>

defer属性是会确保脚本在文档解析完毕后执行的——即便这个脚本在文档解析过程当中就已经下载完毕变成可执行的状态,浏览器也会推迟这个脚本的执行,直到文档解析完毕6,并在DOMContentLoaded以前7

同时,带有defer的脚本彼此之间,能保证其执行顺序。

注意,defer属性并非每一个浏览器支持,即使支持的浏览器,也会由于版本不同致使具体行为不一致。另外,你们能够经过将script标签放置到文档末尾这种简单的作法达到defer属性同样的效果。

defer属性早在IE4就被支持,可是这个defer属性和现代浏览器的行为是有区别的。只有IE10以上,才开始按照标准执行defer属性。

async与defer的影响

参考W3C的官方文档8,defer和async两个属性是能够互相影响的:

There are three possible modes that can be selected using these attributes. If the async attribute is present, then the script will be executed asynchronously, as soon as it is available. If the async attribute is not present but the defer attribute is present, then the script is executed when the page has finished parsing. If neither attribute is present, then the script is fetched and executed immediately, before the user agent continues parsing the page.

简单的概括:

  • 仅有async属性,脚本会异步执行
  • 仅有defer属性,脚本会在文档解析完毕后执行
  • 两个属性都没有,脚本会被同步下载并执行,期间会柱塞文档解析

规范里没有提到两种属性都有时的效果,但这是文档中被容许的。这样的具体效果会在后面讨论。

document.write的影响

docuemnt.write容许向打开的文档流中写入文档内容;内嵌到HTML里面的docuemnt.write能够就地添加文档内容。考虑到docuemnt.write写入script标签的状况9:

<!-- HTML code -->
<script  src="https://snipt.net/raw/7b08744009c450e07c0bfc1d606fc72e/"></script>
<script>document.write("\<script  src=https://snipt.net/raw/a2e8c05c1f6fc0e47d259aa899304e89 \/\>\<\/script\>");</script>
<script  src="https://snipt.net/raw/4fab3017d3d46cbfc4bbd88aab006650/"></script>

观察到执行顺序和普通的script标签没有区别。即便你插入的标签带有async或defer,其行为也是没有区别的。

让人纠结的是反过来10使用。因为第二个脚本是经过document.write写入的。被延迟的脚本在执行时,document已经关闭,document.write是没有任何效果的。因此,无论使用defer仍是async,第二个脚本始终没有运行。

 

原文:

http://pij.robinqu.me/Browser_Scripting/Document_Loading/ScriptTag.html

相关文章
相关标签/搜索