JS 与 CSS 对 DOM 构建的影响
JavaScript 和 CSS 如何影响 DOM 树的解析与渲染
问题
JavaScript 和 CSS 是如何影响 DOM 树构建的?
解答
基本原理
CSS 不会阻塞 DOM 的解析,但会影响 JavaScript 的运行。JavaScript 会阻止 DOM 树的解析,而 CSS(CSSOM)会影响渲染树的生成。
内联 JavaScript 脚本
<html>
<body>
<div>1</div>
<script>
let div1 = document.getElementsByTagName('div')[0]
div1.innerText = 'time.geekbang'
</script>
<div>test</div>
</body>
</html>
当解析到 <script> 标签时,HTML 解析器暂停工作,JavaScript 引擎介入并执行脚本。脚本执行完成后,HTML 解析器恢复解析,继续处理后续内容。
外部 JavaScript 文件
// foo.js
let div1 = document.getElementsByTagName('div')[0]
div1.innerText = 'time.geekbang'
<html>
<body>
<div>1</div>
<script type="text/javascript" src='foo.js'></script>
<div>test</div>
</body>
</html>
执行流程与内联脚本相同,但需要先下载 JavaScript 文件。文件下载过程会阻塞 DOM 解析,下载时间受网络环境和文件大小影响。
浏览器优化: Chrome 会开启预解析线程,提前分析 HTML 中的 JavaScript、CSS 文件并下载。
优化策略:
使用 async 或 defer 属性实现异步加载:
<script async type="text/javascript" src='foo.js'></script>
<script defer type="text/javascript" src='foo.js'></script>
async:脚本并行加载,加载完成后立即执行,执行时机不确定,可能阻塞 HTML 解析,在load事件前执行defer:脚本并行加载,等待 HTML 解析完成后按加载顺序执行,在DOMContentLoaded事件前执行
CSS 样式的影响
/* theme.css */
div {color: blue}
<html>
<head>
<link rel="stylesheet" href='theme.css'>
</head>
<body>
<div>1</div>
<script>
let div1 = document.getElementsByTagName('div')[0]
div1.innerText = 'time.geekbang' // 需要 DOM
div1.style.color = 'red' // 需要 CSSOM
</script>
<div>test</div>
</body>
</html>
当 JavaScript 代码操作 CSSOM(如 div1.style.color)时,必须先解析所有 CSS 样式。如果引用了外部 CSS 文件,需要等待文件下载并解析生成 CSSOM 后,才能执行 JavaScript。
由于渲染引擎无法预知 JavaScript 是否会操作 CSSOM,遇到脚本时都会先执行 CSS 文件的下载和解析,因此样式文件会阻塞 JavaScript 的执行。
关键点
- CSS 不阻塞 DOM 解析,但会阻塞 JavaScript 执行
- JavaScript 会阻塞 DOM 树的解析和构建
- 外部 JavaScript 文件的下载过程会阻塞 DOM 解析
- 使用
async或defer可实现脚本异步加载,避免阻塞 - CSS 文件会阻塞 JavaScript 执行,因为 JavaScript 可能操作 CSSOM
目录