页面生命周期
[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
。它们俩的意义实际上是相同的。 - 当所有资源(
iframe
和img
)都加载完成后,document.readyState
变成complete
。这里我们可以发现,它与img.onload
(img
是最后一个资源)和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
用于独立脚本,例如计数器或广告,这些脚本的相对执行顺序无关紧要。