关于javascript:加载和执行脚本的顺序

load and execute order of scripts

在HTML页面中包含javascript的方法有很多种。我知道以下选项:

  • 内联代码或从外部URI加载
  • 包括在或标签中[1,2]
  • 没有,deferasync属性(只有外部脚本)
  • 包含在静态源中或由其他脚本动态添加(在不同的分析状态下,使用不同的方法)

不算硬盘上的browserscripts,javascript:uri和onEvent属性[3],已经有16种方法可以执行JS,我肯定我忘记了一些事情。

我不太关心快速(并行)加载,我更好奇执行顺序(这可能取决于加载顺序和文档顺序)。是否有一个很好的(跨浏览器)参考,真正涵盖所有案例?例如http://www.websiteoptimization.com/speed/tweak/defer/只处理其中的6个,测试的大多是旧的浏览器。

我担心没有,这是我的具体问题:我有一些(外部)头脚本用于初始化和脚本加载。然后在正文的末尾有两个静态的内联脚本。第一个允许脚本加载器动态地将另一个脚本元素(引用外部JS)附加到主体。第二个静态内联脚本希望使用添加的外部脚本中的JS。它是否可以依赖另一个已被执行(以及为什么是-)?


如果您没有动态加载脚本或将其标记为deferasync,那么脚本将按照页面中遇到的顺序加载。不管是外部脚本还是内联脚本——它们是按照页面中遇到的顺序执行的。保留外部脚本之后的内联脚本,直到加载和运行之前的所有外部脚本。

异步脚本(不管如何指定为异步)以不可预知的顺序加载和运行。浏览器可以并行加载它们,并且可以自由地按任意顺序运行它们。

多个异步对象之间没有可预测的顺序。如果需要一个可预测的顺序,那么就必须通过注册异步脚本中的加载通知来对其进行编码,并在加载适当的内容时手动对javascript调用进行排序。

动态插入脚本标记时,执行顺序的行为将取决于浏览器。您可以在本文中看到火狐的行为。简而言之,新版本的firefox默认将一个动态添加的脚本标记设置为async,除非脚本标记已设置为其他类型。

带有async的脚本标记可以在加载后立即运行。实际上,浏览器可能会暂停解析器执行其他任何操作,并运行该脚本。所以,它几乎可以在任何时候运行。如果脚本被缓存,它几乎可以立即运行。如果脚本需要一段时间来加载,它可能在解析器完成后运行。用async要记住的一点是,它可以随时随地运行,而且时间是不可预测的。

带有defer的脚本标记等待整个解析器完成,然后按遇到的顺序运行用defer标记的所有脚本。这允许您将相互依赖的几个脚本标记为defer。它们都将被推迟到文档分析器完成之后,但它们将按照遇到的顺序执行,以保留它们的依赖项。我认为defer就像脚本被放到一个队列中,在解析器完成之后,这个队列将被处理。从技术上讲,浏览器可能随时在后台下载脚本,但在解析器完成对页面的解析、解析和运行任何未标记deferasync的内联脚本之前,浏览器不会执行或阻止解析器。

这是那篇文章的引言:

script-inserted scripts execute asynchronously in IE and WebKit, but
synchronously in Opera and pre-4.0 Firefox.

HTML5规范的相关部分(对于较新的兼容浏览器)在这里。这里有很多关于异步行为的文章。显然,这个规范不适用于旧的浏览器(或错误确认的浏览器),您可能需要测试这些浏览器的行为来确定。

HTML5规范的引用:

Then, the first of the following options that describes the situation
must be followed:

If the element has a src attribute, and the element has a defer
attribute, and the element has been flagged as"parser-inserted", and
the element does not have an async attribute
The element must be added
to the end of the list of scripts that will execute when the document
has finished parsing associated with the Document of the parser that
created the element.

The task that the networking task source places on the task queue once
the fetching algorithm has completed must set the element's"ready to
be parser-executed" flag. The parser will handle executing the script.

If the element has a src attribute, and the element has been flagged
as"parser-inserted", and the element does not have an async attribute
The element is the pending parsing-blocking script of the Document of
the parser that created the element. (There can only be one such
script per Document at a time.)

The task that the networking task source places on the task queue once
the fetching algorithm has completed must set the element's"ready to
be parser-executed" flag. The parser will handle executing the script.

If the element does not have a src attribute, and the element has been
flagged as"parser-inserted", and the Document of the HTML parser or
XML parser that created the script element has a style sheet that is
blocking scripts The element is the pending parsing-blocking script of
the Document of the parser that created the element. (There can only
be one such script per Document at a time.)

Set the element's"ready to be parser-executed" flag. The parser will
handle executing the script.

If the element has a src attribute, does not have an async attribute,
and does not have the"force-async" flag set The element must be added
to the end of the list of scripts that will execute in order as soon
as possible associated with the Document of the script element at the
time the prepare a script algorithm started.

The task that the networking task source places on the task queue once
the fetching algorithm has completed must run the following steps:

If the element is not now the first element in the list of scripts
that will execute in order as soon as possible to which it was added
above, then mark the element as ready but abort these steps without
executing the script yet.

Execution: Execute the script block corresponding to the first script
element in this list of scripts that will execute in order as soon as
possible.

Remove the first element from this list of scripts that will execute
in order as soon as possible.

If this list of scripts that will execute in order as soon as possible
is still not empty and the first entry has already been marked as
ready, then jump back to the step labeled execution.

If the element has a src attribute The element must be added to the
set of scripts that will execute as soon as possible of the Document
of the script element at the time the prepare a script algorithm
started.

The task that the networking task source places on the task queue once
the fetching algorithm has completed must execute the script block and
then remove the element from the set of scripts that will execute as
soon as possible.

Otherwise The user agent must immediately execute the script block,
even if other scripts are already executing.


浏览器将按找到脚本的顺序执行脚本。如果调用外部脚本,它将阻塞页面,直到脚本被加载和执行。

要测试这一事实:

1
2
3
4
5
6
// file: test.php
sleep(10);
die("alert('Done!');");

// HTML file:
<script type="text/javascript" src="test.php">

动态添加的脚本在附加到文档后立即执行。

要测试这一事实:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE HTML>
<html>
<head>
    Test
</head>
<body>
    <script type="text/javascript">
        var s = document.createElement('script');
        s.type ="text/javascript";
        s.src ="link.js"; // file contains alert("hello!");
        document.body.appendChild(s);
        alert("appended");
   
    <script type="text/javascript">
        alert("final");
   
</body>
</html>

警报顺序为"附加"->"你好!">"最后"

如果在脚本中,您试图访问尚未到达的元素(例如:do something with #blah),则会得到一个错误。

总的来说,是的,您可以包括外部脚本,然后访问它们的函数和变量,但是只有当您退出当前的标记并开始一个新的标记时。


@addyosmani的精彩总结

enter image description here

不知羞耻地从https://addyosmani.com/blog/script-priorities复制/