在 HTML 中会遇到以下三类 script:
<script src='xxx'></script><script src='xxx' async></script><script src='xxx' defer></script>
前提
- 如果脚本是内联的,浏览器会先去执行这段内联的脚本,如果是外链的,那么先会去加载脚本,然后执行。
- async和defer模型不适用于内联脚本
默认的模式
- 普通JS的下载和解析都会影响DOM解析
async模式
-
在执行到加了async的script时会先下载,然后再去执行下一个标签。待到这个script的外链下载完毕时,如果DOM正在渲染则暂停,执行async script的内容。多个async先下载完的先执行
-
async和 DOMContentLoaded 无任何绑定关系。
- 也有可能html先解析完毕了,script还没下载完,会出现以下情况
defer模式
-
在 defer 模式下,当浏览器解析到带有 defer 属性的
<script>标签时,会开始在后台下载脚本[多个 defer 脚本会并行下载],但不会阻塞后续 HTML 的解析。待 HTML 解析完成后,浏览器会等待所有 defer 脚本下载完毕,然后按照它们在 HTML 中出现的顺序依次执行这些脚本,等所有 defer 脚本执行完毕后,才会触发 DOMContentLoaded 事件。
如果defer的script也放在</body>前一行,则绿色走完才走紫色和红色。此时对DOM渲染和DOMContentLoaded的时机的触发和普通script完全相同。
总结
| script 标签 | JS 执行顺序 | 是否阻塞解析 HTML | DOMContentLoaded回调 |
|---|---|---|---|
<script> | 依次 | 是 | 等待 |
<script async> | 先下载完先执行 | DOM未解析完时阻塞 | 不等待 |
<script defer> | 先下载完所有defer再依次执行 | 否 | 等待 |
动态插入的script
- 动态插入的 JavaScript(js)默认是异步加载模式。
- 常见的实现方式是动态创建
<script>标签并将其添加到 HTML 文档中。这种方式可以使脚本在页面加载过程中异步下载和执行,不会阻塞页面的其他内容的呈现。
function loadScript(url) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
document.head.appendChild(script);
}
// 调用函数加载外部脚本
loadScript('your_script.js');
- 动态加载的脚本与普通的脚本一样,也可以使用
defer或async属性来进一步控制其加载和执行行为(但需要注意浏览器的兼容性,本文不细述)- 如果添加了
defer属性,浏览器会在文档解析完成后,按照脚本在文档中出现的顺序依次执行它们。 - 如果添加了
async属性,脚本会在下载完成后尽快执行,而不考虑文档的解析顺序,执行时机可能在DOMContentLoaded事件之前或之后。
- 如果添加了
本文参考链接: zhuanlan.zhihu.com/p/622763093