浏览器渲染原理
渲染时间点
1. 解析 HTML - Parse HTML
每一个样式表会形成一个 CSStyleSheet 节点
<style></style>
<link ...>
<div style="color: red">
- 浏览器默认样式表
body、div 等为什么是 block?因为默认样式表中设置了
display: block
head 为什么看不见?因为默认样式表中设置了
display: none
除了默认样式,其他样式 JS 都能操作
行内样式直接使用 dom.style
操作
<style></style>
和 <link ...>
可以在控制台中使用 CSSOM 树 document.styleSheets
操作
将网页中所有 div 都加边框:document.styleSheets[0].addRule('div', 'border: 2px solid #f40 !important')
HTML 解析过程中遇到 CSS 代码怎么办?
为了提高解析效率,浏览器会启动一个预解析器率先下载和解析 CSS
如果渲染主线程解析到 link
位置,此时外部的 CSS 文件还没下载解析好,主线程不会等待
如果解析到 script
位置,会停止解析 HTML,转而等待 JS 文件下载好,并将全局代码解析执行完成后,才能继续解析 HTML,因为 JS 代码中可能会操作 DOM 树
2. 样式计算 - Recalculate Style
会计算出最终样式(也叫计算后的样式,Computed Style)
自己写的样式可能很少,但最终样式可能会很多
在这一过程中,很多预设值会变成绝对值,比如 red
会变成 rgb(255,0,0)
相对单位会变成绝对单位,比如 em
会变成 px
这一步完成后,会得到一棵带有样式的 DOM 树
这个阶段算不出来相对单位转换后的绝对数值,比如 width: 70%
在这个阶段算不出来
JS 如何获取最终样式?getComputedStyle()
3. 布局 - Layout
根据 DOM 树(带样式)算出每个元素的位置、尺寸等,形成 Layout 树(包含每个节点的几何信息)
位置是相对包含块的位置(不是父级的位置)
比如说三个嵌套的盒子,最外面的盒子定位了,那么最里面的盒子的位置是相对已定位的盒子(最外面的盒子)的,而不是相对它的父级
同理,width: 70%
也是相对包含块的宽度
DOM 树和 Layout 树并不是一一对应的
JS 中不能直接获取 Layout 树的对象,但是能间接获取一些信息:
document.body.clientWidth
4. 分层 - Layer
与堆叠上下文有关的属性(如 z-index
)可能会影响分层决策
可以把一些经常变化的元素分为一层,将来变动时只会变动这层的内容
可以使用 will-change
告诉浏览器「这个元素稍后可能会发生某些变化,请提前做好准备!」
will-change
是图层管理的开发接口,允许开发者精细化控制渲染性能,直接影响浏览器渲染管线的优化策略
.box {
will-change: transform;
transition: transform 0.3s;
}
.box:hover {
transform: scale(1.2);
}
-
无
will-change
元素在首次
transform
变化时,浏览器才创建图层 → 可能导致首帧卡顿 -
有
will-change
浏览器在样式计算阶段就分配独立图层 → 动画开始时直接走 GPU 合成
5. 绘制 - Paint
这里的绘制是指生成绘制的指令
类似于 Canvas
渲染主线程的工作到此为止,剩余步骤交给其他线程完成
6. 分块 - Tiling
分块会将每一层分为多个小的区域
会先画靠近视口的块
合成线程会从线程池中拿取多个线程来完成分块工作
7. 光栅化 - Raster
光栅化是将每个块变成位图(也就是变为每个像素点的信息)
优先处理靠近视口的块
光栅化是在 GPU 中做的,所以要用到 GPU 进程
8. 画 - Draw
合成线程计算出每个位图在屏幕上的位置,生成 quad
(指引信息),交给 GPU 进行最终呈现
为什么要用 GPU 进程中转一下?明明可以直接通知 GPU 的
因为渲染主线程和合成线程处于渲染进程中,渲染进程是在沙盒中的,隔离了硬件,因此必须通过 GPU 进程中转才能通知到 GPU
指引会标识出每个位图应该画到屏幕的哪个位置,以及会考虑到旋转、缩放等变形
为什么说 transform
效率高?因为 transform
、opacity
等发生在合成线程,与渲染主线程无关
transform
不会改变元素的布局信息,也就是说,不会触发回流(Reflow),它只是在渲染的最终阶段对元素的视觉输出进行变形
如 transform: translateX(50px)
,视觉上,浏览器在合成阶段将该元素整体平移 50px,但布局系统不知道这个变化
只有 transform
和 opacity
能完全避免回流(Reflow)