defer 属性
为了解决脚本文件下载阻塞网页渲染的问题, 一个方法是对 < script > 元素加入 defer 属性. 它的作用是延迟脚本的执行, 等到 DOM 加载生成后, 再执行脚本.
- <script src="a.js" defer>
- </script>
- <script src="b.js" defer>
- </script>
上面代码中, 只有等到 DOM 加载完成后, 才会执行 a.JS 和 b.JS.
defer 属性的运行流程如下.
浏览器开始解析 html 网页.
解析过程中, 发现带有 defer 属性的 < script > 元素.
浏览器继续往下解析 HTML 网页, 同时并行下载 < script > 元素加载的外部脚本.
浏览器完成解析 HTML 网页, 此时再回过头执行已经下载完成的脚本.
有了 defer 属性, 浏览器下载脚本文件的时候, 不会阻塞页面渲染. 下载的脚本文件在 DOMContentLoaded 事件触发前执行 (即刚刚读取完 </HTML > 标签), 而且可以保证执行顺序就是它们在页面上出现的顺序.
对于内置而不是加载外部脚本的 script 标签, 以及动态生成的 script 标签, defer 属性不起作用. 另外, 使用 defer 加载的外部脚本不应该使用 document.write 方法.
async 属性
解决 "阻塞效应" 的另一个方法是对 < script > 元素加入 async 属性.
- <script src="a.js" async>
- </script>
- <script src="b.js" async>
- </script>
async 属性的作用是, 使用另一个进程下载脚本, 下载时不会阻塞渲染.
浏览器开始解析 HTML 网页.
解析过程中, 发现带有 async 属性的 script 标签.
浏览器继续往下解析 HTML 网页, 同时并行下载 < script > 标签中的外部脚本.
脚本下载完成, 浏览器暂停解析 HTML 网页, 开始执行下载的脚本.
脚本执行完毕, 浏览器恢复解析 HTML 网页.
async 属性可以保证脚本下载的同时, 浏览器继续渲染. 需要注意的是, 一旦采用这个属性, 就无法保证脚本的执行顺序. 哪个脚本先下载结束, 就先执行那个脚本. 另外, 使用 async 属性的脚本文件里面的代码, 不应该使用 document.write 方法.
defer 属性和 async 属性到底应该使用哪一个?
一般来说, 如果脚本之间没有依赖关系, 就使用 async 属性, 如果脚本之间有依赖关系, 就使用 defer 属性. 如果同时使用 async 和 defer 属性, 后者不起作用, 浏览器行为由 async 属性决定.
脚本的动态加载
- <script > 元素还可以动态生成, 生成后再插入页面, 从而实现脚本的动态加载.
- ['a.js', 'b.js'].forEach(function(src) {
- var script = document.createElement('script');
- script.src = src;
- document.head.appendChild(script);
- });
这种方法的好处是, 动态生成的 script 标签不会阻塞页面渲染, 也就不会造成浏览器假死. 但是问题在于, 这种方法无法保证脚本的执行顺序, 哪个脚本文件先下载完成, 就先执行哪个.
如果想避免这个问题, 可以设置 async 属性为 false.
- ['a.js', 'b.js'].forEach(function(src) {
- var script = document.createElement('script');
- script.src = src;
- script.async = false;
- document.head.appendChild(script);
- });
上面的代码不会阻塞页面渲染, 而且可以保证 b.JS 在 a.JS 后面执行. 不过需要注意的是, 在这段代码后面加载的脚本文件, 会因此都等待 b.JS 执行完成后再执行.
如果想为动态加载的脚本指定回调函数, 可以使用下面的写法.
- function loadScript(src, done) {
- var JS = document.createElement('script');
- JS.src = src;
- JS.onload = function() {
- done();
- };
- JS.onerror = function() {
- done(new Error('Failed to load script' + src));
- };
- document.head.appendChild(JS);
- }
参考文件: https://wangdoc.com/javascript/bom/engine.html
来源: https://www.cnblogs.com/whdaichengxu/p/11302048.html