如果script标签是引用的外部js文件, 那就会有一个下载js文件这一过程, 为了不因为这个下载过程而阻塞页面解析与渲染, 我们需要一种机制来解决这一问题, 方法之一就是使用 defer和async属性.
方法1. 使用 defer属性
defer属性的作用是延迟脚本的执行, 只有等到DOM生成之后才会执行脚本. 类似在DOMContentLoaded事件下添加监听函数.
// test1.js // console.log(document.body.nodeName); // test1.html <!DOCTYPE html> <html lang="zh"> <head> <script defer src="./test1.js"></script> </head> <body> </body> </html>
defer 属性的运行流程主要有下面几步:
1. 浏览器下载的同时解析html
2. 发现带有defer的script标签时, 会在下载html的同时下载这个script标签的外部js文件
3. 下载并解析html完成后会触发 DOMContentLoaded事件, 但在这个事件触发之前, 带defer的js文件就会开始执行.
注意:
1. 多个带defer的script标签的最终执行顺序是跟它们在html中出现的先后顺序严格对应的
2. 对于不是加载外部js文件和动态生成的script文件, defer属性不生效
3. 使用defer加载的外部脚本不应该使用document.write()方法.
方法2. 使用async属性
async属性和defer属性都能解析加载script外部js文件引起的阻塞问题, 不过defer属性是在DOM载入完成以后才会执行, 而async是另开一个进程去下载脚本, 下载完成以后立刻执行, 执行的时候是会暂停解析的.
也就是说: async解决阻塞的方法是: 另开一个进程下载脚本, 这样就不会阻塞主进程html网页的解析.
// test1.js // console.log("defer是DOM载入完成后执行.") // test2.js // console.log("async是新开一个进程, 下载完成后就暂停主进程的解析, 执行下载的脚本.") <!DOCTYPE html> <html lang="zh"> <head> <script defer src="./test1.js"></script> <script async src="./test2.js"></script> </head> <body> </body> </html>
注意:
1. 使用async不能保证脚本的执行顺序, 而是谁先下载完, 就先执行谁, 因此async适用于脚本直接没有依赖关系的情况. 反之在用defer;
2. 如果一个scritp标签同时有defer和async属性, 则defer失效, script的行为由async决定;
3. 在脚本中还是不能使用document.write()方法.
总结起来, defer和async区别在于, 前者是在html解析完毕后按顺序执行, 而async是单独下载, 完成后立即执行.