更多个人博客:(https://github.com/zenglinan/blog)
如果对你有帮助,欢迎star。
浏览器对页面的渲染:
渲染过程
- 处理 HTML 标记并构建 DOM 树。
- 处理 CSS 标记并构建 CSSOM 树。
- 将 DOM 与 CSSOM 合并成一个渲染树。
- 根据渲染树来布局,计算每个节点的几何信息。
- 将各个节点绘制到屏幕上。
但上述过程不一定会按顺序执行下来, 可能会被阻塞。
假如 DOM CSSOM 被修改, 以上过程会重复执行
阻塞渲染
CSS阻塞
CSS HTML 都是阻塞页面渲染的因素之一, 因为渲染树需要同时具有 DOM 树、 CSSOM树, 才能开始构建
浏览器解析 HTML 时,遇到了 CSS 资源, 会并行解析和构建 CSSOM DOM
所以 CSS 不会阻塞 HTML 的解析, 但是会阻塞渲染树的构建
所以需要尽可能快地提供 CSS 样式, 尽量放在 head 标签内。
但是: CSS 也会阻塞 JS 脚本的执行!!
有这么一种情况, CSS 解析不是不影响 HTML 解析嘛, CSS 解析过程中, HTML 解析到一个 script 标签, 这时会去加载这个脚本, 但加载完之后必须等到前面的 CSSOM 构建完成才会执行
JS阻塞
遇到 JS 脚本的时候, 会阻塞 HTML 解析, 马上去加载脚本, 加载完后就执行
改变 script 脚本阻塞模式的 defer async
页面解析时, 遇到 script 脚本会阻塞 HTML 的解析, 马上去加载脚本, 并且加载完后就马上执行。
但是浏览器会提前偷看后面要下载的资源先进行预加载
defer async 可以改变这种阻塞模式, 注意: defer async 对 inline-script 无效
defer
文档按顺序解析,遇到脚本时下载,下载过程中文档继续解析,文档解析完成后开始执行下载好的脚本,脚本执行有先后顺序
async
文档按顺序解析,遇到脚本时下载,下载过程中文档继续解析,下载完成后停止 HTML 解析, 开始执行下载好的脚本,脚本执行无先后顺序
小结一下
- CSS 不会阻塞 DOM 的解析, 但会阻塞 DOM 的渲染, 会阻塞 JS 的执行
- JS 会阻塞 DOM 的解析和渲染, 但浏览器会偷看后面要下载的资源进行加载, 可以用 defer async 来避免阻塞。
最佳实践 :
CSS 资源放在 head 标签里尽快加载, 避免阻塞渲染树的构建, 也避免阻塞 JS 执行, 以及页面样式从无到突然出现闪动。
JS 脚本用 defer async 避免堵塞。
对于不需要操作 DOM , 且互相不依赖 (即无所谓脚本先后执行顺序) 的脚本, 用 async
对于需要操作 DOM, 且互相依赖 (在乎脚本先后执行顺序) 的脚本, 用 defer
番外: 普通的 script 标签为什么要放在 body 底部?
为了防止 JS 加载执行时阻塞 DOM 的解析, 造成白屏时间过长, 同时也有可能 script 脚本需要操作 DOM, 需要等到 DOM 解析好了才执行。