外观
浏览器怎么把页面画出来?
HTML 解析成 DOM,CSS 解析成 CSSOM,两者合成渲染树,然后布局、绘制、合成。同步脚本会挡住 HTML 解析;CSS 会挡住渲染,也会挡住它后面的同步脚本。
原理
解析 HTML 时遇到资源会交给预加载扫描器提前下载。构建出的渲染树只包含要显示的节点,display: none 的节点不在里面,visibility: hidden 仍占位置。
接下来三步:
- 布局(回流):算出每个节点的几何信息。
- 绘制(重绘):生成绘制指令,例如背景、文字、边框。
- 合成:把图层交给 GPU 合到屏幕上。
transform和opacity通常只走合成,不必重新布局和绘制。
改宽度、位置会触发布局,往往连带重绘。只改颜色通常只重绘。连续读 offsetWidth 又写 style,会迫使浏览器提前布局,这就是强制同步布局。
脚本的三种加载:
| 写法 | 下载 | 执行时机 | 对解析的影响 |
|---|---|---|---|
默认 script | 遇到就下载 | 马上执行 | 挡住解析 |
async | 并行下载 | 下完就执行,顺序不定 | 执行时暂停解析 |
defer | 并行下载 | 文档解析完后按顺序执行 | 不挡住解析 |
DOMContentLoaded 在文档解析完成、defer 脚本执行完之后触发,不等图片。它前面的同步脚本、以及这些脚本所等待的样式表,会把它推迟。load 要等样式、图片等子资源都加载完。
追问
CSS 放头部、JS 放底部,现在还对吗?
样式仍放头部,避免先画出没样式的 HTML。脚本优先用 defer 放在头部,既不挡解析,又能保证顺序。需要尽早执行的独立脚本才用 async。
display: none 和 visibility: hidden 的布局差别?
display: none 不生成盒子,不参与布局。visibility: hidden 看不见,但盒子还在。
易错点
- 渲染树不是 DOM 树的别名,它还要结合 CSS,并丢掉不显示的节点。
async不能保证执行顺序,有依赖的脚本不要用它。- 动画能用
transform/opacity解决时,不要改top、left、width。