重读红宝书系列之异步脚本

254 阅读2分钟

前言

​ 最近Adrian在重读第四版红宝书。第二章中2.1节讲到了script标签的8个属性,其中的'async'和'defer'是关于异步脚本的,这部分之前还比较陌生,于是来学习总结一下。

预备知识

解析HTML

  1. 在解析的过程中如果碰到了script标签分为三种情况(规范中定义的)

    标签属性加载是否阻塞HTML解析解析是否阻塞HTML解析执行时机多个脚本
    <script>随着HTML解析的进度顺序执行顺序执行
    <script defer>HTML解析完成后,DCL事件前执行在HTML解析完成后顺序执行
    <script async>加载完成后立即执行,Load事件之前顺序不固定

    ⚠️ script标签前面的CSS如果没有加载完,JS的执行会阻塞。因为JS和CSS都可以对样式进行修改,那么只有在保证执行顺序的情况下才可以正确渲染。

    图解

  2. 然而在实际中,有的现代浏览器会按照优先级预加载资源,并不是解析到了script标签才去请求资源,这样做减少了资源请求的时间。在Chrome中,因为脚本和样式文件具有阻塞性(JS可能会阻塞HTML解析,CSS不会阻塞HTML解析,但是会阻塞页面渲染),所以它们的优先级更高。图片等其他二进制资源后续加载。所有的资源都是在一开始解析HTML的时候就开始加载的。

如何使用

defer

红宝书中这样说

推迟执行的脚本不一定会总会按顺序发生或者在DCL事件执行之前执行,因此最好只包含一个这样的脚本

是因为各个浏览器在优化的过程中可能会偏离规范,有的浏览器会忽略这个属性。

async

  • 由于async的执行顺序不固定,所以要求脚本之间是不能相互依赖的,需要是独立的脚本。

🚫defer和async都属于异步脚本,不可以在文档中直接调用documnet.write()方法;

💡如果async和defer同时使用会优先使用async,如果浏览器不支持则会降级到defer(因为async是HTML5的特性,而 defer在HTML4中就已经存在)

总结

💡由于大部分现代浏览器会预加载资源,所以在使用时只要遵循以下原则就可以了

  1. CSS放在页面头部,保证它先加载完成不会阻塞JS的运行;
  2. JS放在页面底部,保证了兼容性的同时没有让脚本的加载阻塞到HTML的解析,减少了白屏时间。

参考

你不知道的DOMContentLoaded

浅谈script标签的defer和async

Deciphering the Critical Rendering Path