脚本执行顺序

137 阅读2分钟

页面生命周期

[1] initial readyState:loading [2] readyState:interactive [2] DOMContentLoaded [3] iframe onload [4] img onload [4] readyState:complete [4] window onload

方括号中的数字表示发生这种情况的大致时间。标有相同数字的事件几乎是同时发生的(± 几毫秒)。

  • DOMContentLoaded 之前,document.readyState 会立即变成 interactive。它们俩的意义实际上是相同的。
  • 当所有资源(iframeimg)都加载完成后,document.readyState 变成 complete。这里我们可以发现,它与 img.onloadimg 是最后一个资源)和 window.onload 几乎同时发生。转换到 complete 状态的意义与 window.onload 相同。区别在于 window.onload 始终在所有其他 load 处理程序之后运行。

脚本:async,defer

defer

  • 具有 defer 特性的脚本不会阻塞页面。
  • 具有 defer 特性的脚本总是要等到 DOM 解析完毕,但在 DOMContentLoaded 事件之前执行。

浏览器扫描页面寻找脚本,然后并行下载它们,以提高性能。因此,在上面的示例中,两个脚本是并行下载的。small.js 可能会先下载完成。

……但是,defer 特性除了告诉浏览器“不要阻塞页面”之外,还可以确保脚本执行的相对顺序。因此,即使 small.js 先加载完成,它也需要等到 long.js 执行结束才会被执行。

async

async 脚本就是一个会在加载完成时执行的完全独立的脚本

动态脚本

let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script); // (*)

当脚本被附加到文档 (*) 时,脚本就会立即开始加载。

默认情况下,动态脚本的行为是“异步”的。

  • 它们不会等待任何东西,也没有什么东西会等它们。
  • 先加载完成的脚本先执行(“加载优先”顺序)。

如果我们显式地设置了 script.async=false,则可以改变这个规则。然后脚本将按照脚本在文档中的顺序执行,就像 defer 那样。

顺序DOMContentLoaded
async加载优先顺序。脚本在文档中的顺序不重要 —— 先加载完成的先执行不相关。可能在文档加载完成前加载并执行完毕。如果脚本很小或者来自于缓存,同时文档足够长,就会发生这种情况。
defer文档顺序(它们在文档中的顺序)在文档加载和解析完成之后(如果需要,则会等待),即在 DOMContentLoaded 之前执行。

在实际开发中,defer 用于需要整个 DOM 的脚本,和/或脚本的相对执行顺序很重要的时候。

async 用于独立脚本,例如计数器或广告,这些脚本的相对执行顺序无关紧要。