加载并执行脚本的顺序

Ber*_*rgi 248 javascript load-order

有很多不同的方法可以在html页面中包含JavaScript.我知道以下选项:

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

不计算来自硬盘,javascript:URIs和onEvent-attributes [ 3 ]的浏览器脚本,已经有16个替代方法可以执行JS,我确信我忘了一些东西.

我不太关心快速(并行)加载,我对执行顺序更感兴趣(这可能取决于加载顺序和文档顺序).是否有一个很好的(跨浏览器)参考,涵盖所有情况?例如http://www.websiteoptimization.com/speed/tweak/defer/仅处理其中的6个,并且主要测试旧浏览器.

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

jfr*_*d00 309

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

异步脚本(无论它们如何指定为异步)以不可预测的顺序加载和运行.浏览器并行加载它们,可以按照自己想要的顺序自由运行它们.

多个异步事物之间没有可预测的顺序.如果需要一个可预测的订单,则必须通过从异步脚本注册加载通知并在加载适当的东西时手动排序javascript调用来编码.

当动态插入脚本标记时,执行顺序的行为将取决于浏览器.您可以在本参考文章中看到Firefox的行为方式.简而言之,较新版本的Firefox默认动态添加脚本标记为异步,除非已设置脚本标记.

脚本标记async可以在加载后立即运行.实际上,浏览器可能会将解析器从其正在执行的任何操作中暂停并运行该脚本.所以,它几乎可以随时运行.如果脚本被缓存,它可能几乎立即运行.如果脚本需要一段时间才能加载,它可能会在解析器完成后运行.要记住的一件事async是,它可以随时运行,而且时间不可预测.

脚本标记defer等待整个解析器完成,然后defer按照遇到的顺序运行所有标记的脚本.这允许您标记几个彼此依赖的脚本defer.它们都将被推迟到文档解析器完成之后,但它们将按照它们遇到的顺序执行,保留它们的依赖关系.我想像defer脚本被放入一个队列中,将在解析器完成后处理.从技术上讲,该浏览器可以在任何时间在后台下载的脚本,但它们将不执行或阻止解析器后才解析器完成解析的页面和解析和运行都没有标注任何内嵌脚本deferasync.

以下是该文章的引用:

脚本插入的脚本在IE和WebKit中异步执行,但在Opera和4.0之前的Firefox中同步执行.

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

来自HTML5规范的引用:

然后,必须遵循描述该情况的以下第一个选项:

如果元素具有src属性,并且该元素具有defer属性,并且该元素已被标记为"解析器插入",并且该元素没有async属性 该元素必须添加到列表的末尾当文档完成与创建元素的解析器的Document相关联的解析时将执行的脚本.

一旦获取算法完成,网络任务源在任务队列上放置的任务必须设置元素的"准备好被解析器执行"标志.解析器将处理执行脚本.

如果元素具有src属性,并且该元素已标记为"parser-inserted",并且该元素没有async属性 该元素是创建该元素的解析器Document的挂起解析阻止脚本.(每个文档一次只能有一个这样的脚本.)

一旦获取算法完成,网络任务源在任务队列上放置的任务必须设置元素的"准备好被解析器执行"标志.解析器将处理执行脚本.

如果元素没有src属性,并且元素已被标记为"解析器插入",则创建脚本元素的HTML解析器或XML解析器的Document具有阻塞脚本样式表元素是正在创建元素的解析器的Document的解析阻塞脚本.(每个文档一次只能有一个这样的脚本.)

设置元素的"准备好解析器执行"标志.解析器将处理执行脚本.

如果元素具有src属性,没有async属性,并且没有设置"force-async"标志元素必须添加到脚本列表的末尾,这些脚本将按顺序执行,并尽快关联在准备脚本算法开始时使用脚本元素的Document.

完成提取算法后,网络任务源在任务队列上放置的任务必须执行以下步骤:

如果元素现在不是脚本列表中的第一个元素,它将尽快按顺序执行,那么将元素标记为就绪,但是在不执行脚本的情况下中止这些步骤.

执行:执行与此脚本列表中第一个脚本元素对应的脚本块,该脚本将尽快按顺序执行.

从此脚本列表中删除第一个元素,这些脚本将尽快按顺序执行.

如果这个将尽快按顺序执行的脚本列表仍然不为空且第一个条目已被标记为就绪,则跳回标记为执行的步骤.

如果元素具有src属性必须将元素添加到脚本集中,这些脚本将在准备脚本算法开始时尽快执行脚本元素的Document.

一旦提取算法完成,网络任务源在任务队列上放置的任务必须执行脚本块,然后从将尽快执行的脚本集中删除该元素.

否则,即使其他脚本已在执行,用户代理也必须立即执行脚本块.


Nie*_*sol 11

浏览器将按照找到的顺序执行脚本.如果调用外部脚本,它将阻止页面,直到加载并执行脚本.

为了测试这个事实:

// file: test.php
sleep(10);
die("alert('Done!');");

// HTML file:
<script type="text/javascript" src="test.php"></script>
Run Code Online (Sandbox Code Playgroud)

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

为了测试这个事实:

<!DOCTYPE HTML>
<html>
<head>
    <title>Test</title>
</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>
    <script type="text/javascript">
        alert("final");
    </script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

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

如果在脚本中您尝试访问尚未到达的元素(例如:),<script>do something with #blah</script><div id="blah"></div>那么您将收到错误.

总的来说,是的,您可以包含外部脚本,然后访问它们的功能和变量,但前提是您退出当前<script>标记并开始新标记.

  • 这个答案是不正确的."动态添加的脚本在被附加到文档后立即执行"并非总是如此.有时这是真的(例如对于旧版本的Firefox),但通常情况并非如此.如jfriend00的回答中所述,执行顺序不是确定的. (14认同)
  • link.js没有阻止.使用类似于php的脚本来模拟较长的下载时间. (4认同)
  • 无论脚本是否内联,脚本按照它们在页面上出现的顺序执行是没有意义的。那么为什么 Google 标签管理器片段和我见过的许多其他标签管理器片段都有代码来在页面中的所有其他脚本标签之上插入新脚本呢?如果上述脚本确实已经加载,那么这样做就没有意义了?或者我错过了什么。 (2认同)

Fli*_*ion 8

在测试了许多选项后,我发现以下简单的解决方案是按照在所有现代浏览器中添加的顺序加载动态加载的脚本

loadScripts(sources) {
    sources.forEach(src => {
        var script = document.createElement('script');
        script.src = src;
        script.async = false; //<-- the important part
        document.body.appendChild( script ); //<-- make sure to append to body instead of head 
    });
}

loadScripts(['/scr/script1.js','src/script2.js'])
Run Code Online (Sandbox Code Playgroud)