浏览器在构建页面时会并行处理 DOM 和 CSS,之后将 DOM 树和 CSSOM 树组合成 Render 树,遍历 Render 树布局和绘制元素:
-
调用 HTML 解析器解析 HTML 构建 DOM 树;
-
调用 CSS 解析器解析 CSS 构建 CSSOM 树;
-
前两步解析过程并行执行,DOM 树和 CSSOM 树组合成 Render 树,再之后用 Render 树渲染、布局、绘制。
JS 文件在加载时会造成阻塞。HTML 解析器在 DOM 解析过程中遇到 JS 会停止解析,转而去加载并执行 JS,JS 执行完成后再继续解析 DOM。
原因: 历史遗留问题,早期的 JS 大量使用 document.write 方法,如果等 DOM 解析完再执行 JS,会造成大范围的回流,对性能影响太大。
如何避免:
-
使用
<script>
引用 JS 文件可以附带 defer 属性,该属性使 JS 文件在 DOM 解析时并行下载,DOM 解析完成之后再执行。 -
将
<script>
标签放在闭合的</body>
标签之前,使 HTML 解析器遇到 JS 时已解析完 DOM。
CSS 文件的引入位置决定其是否会造成阻塞,分多种情况:
-
CSS 不会阻塞 DOM 解析,因为 HTML 解析器构建 DOM 树和 CSS 解析器构建 CSSOM 树是并行的。
-
CSS 会阻塞 DOM 渲染,因为 DOM 渲染需要 DOM 树和 CSSOM 树组合成 Render 树,有 Render 树之后才开始渲染。
-
CSS 会阻塞 JS 执行,因为 JS 可能会改变 CSS 中的内容,所以在 CSS 之后的 JS 会等待 CSS 加载完再执行。