CSS、JS 阻塞 DOM 解析渲染
为什么
<script>最好放底部,<style>最好放头部?如果头部同时有<script>与<link>,最好将<script>放在<link>上面吗?
1. CSS
CSS 不会阻塞 DOM 解析
浏览器解析 DOM 生成 DOM Tree,结合 CSS 生成的 CSS Tree,最终组成 Render Tree 再渲染页面。在这个过程中,CSS 完全无法影响 DOM Tree,因此无需阻塞 DOM 解析。
CSS 阻塞页面渲染
DOM Tree 和 CSS Tree 会组合成 Render Tree。如果 CSS 不阻塞渲染,那么 CSS 下载之前浏览器就会渲染出"原始"页面,下载完成后再变成"完整"页面——用户体验差且渲染成本高。
基于性能与用户体验的考虑,浏览器会尽量减少渲染次数,所以 CSS 阻塞页面渲染。
特殊例子
<header>
<link rel="stylesheet" href="/css/sleep3000-common.css">
<script src="/js/logDiv.js"></script>
</header>
浏览器会转圈圈三秒,期间不会打印任何东西,之后呈现浅蓝色 div,再打印 null。
这是 CSS 阻止了 DOM 解析吗?不是,是 <script> 阻塞。
原因:脚本如果获取元素的样式(依赖 CSS),浏览器需要先计算样式再执行 JS。浏览器无法预知脚本内容,为避免样式错位,只好等前面所有样式下载完再执行 JS。
2. JS
<script> 阻塞 DOM 解析
浏览器不知道脚本的内容,如果先解析下面的 DOM,万一脚本删除/重写了后面的节点,浏览器就白干活了。浏览器无法预估内容,干脆全部停住,等脚本执行完再继续。
优化
- 体积大且不阻塞首屏:加
defer或async:defer:等 DOM 解析完再执行,按顺序执行(适合依赖 DOM 的脚本)。async:下载完立刻执行,不保证顺序(适合独立脚本,如统计 SDK)。
- 执行时间长:拆分代码,不用立即执行的可以
setTimeout。 - 现代浏览器会预读后面的 DOM,提前下载
link、script、img等资源,不会等到解析到那里再下载。
浏览器遇到 <script> 时会触发页面渲染
这是解释"JS 执行会等待 CSS 下载"的根因:浏览器不知道脚本内容,碰到脚本就渲染一次页面,确保脚本能拿到最新的 DOM 信息(即使脚本并不需要)。
总结:
- CSS:阻塞渲染,不阻塞 DOM 解析;执行 JS 时若依赖 CSS 会被同步阻塞。
- JS:阻塞 DOM 解析(无 defer/async);执行前后都可能触发一次渲染。
来源整理自:我的有道云笔记



