一、 浏览器渲染原理
通过网络获取html文件,然后解析html文件,最后渲染在浏览器上给用户看到。
二、渲染的时间点
浏览器通过网络进程进行通信获取到html文件,然后在消息队列中创建渲染任务进行排队,最后交给渲染主线程进行渲染。
三、渲染流水线
1. 解析HTML - Parse HTML
HTML会被解析成DOM树和CSSOM树
DOM树
CSSOM树
HTML 解析过程中遇到 CSS 代码怎么办?
为了提⾼解析效率,浏览器会启动⼀个预解析器率先下载和解析 CSS
HTML 解析过程中遇到 JS 代码怎么办?
渲染主线程遇到 JS 时必须暂停⼀切⾏为,等待下载执⾏完后才能继续,预解析线程可以分担⼀点下载 JS 的任务
2. 样式计算 - Recalculate Style
根据解析出来的dom树和cssom树进行样式计算,将dom树和cssom树结合在一起
3. 布局 - Layout
根据结合的dom树和cssom树,会生成一个layout树
其中,dom树和layout树不是一一对应的。例如:样式中有display: none、伪元素或者html树中有文本等,都会使dom树和layout树不一样
4. 分层 - Layer
浏览器会根据layout树进行分析对内容分层,哪一部分是会进行频繁改变和哪一部分是相对稳定的,将会分层出来
5. 绘制 - Paint
根据计算的分层,为每一层生成如何的绘制指令。
渲染主线程的工作到此为止,剩余步骤交给其他线程完成。
6. 分块 - Tiling
分块会将每一层分为多个小的区域
分块的工作是交给多个线程同时进行的
7. 光栅化 - Raster
光栅化是将每个块变成位图,优先处理靠近视口的块
此过程会用到GPU加速
8. 画 - Draw
合成线程计算出每个位图在屏幕上的位置,交给GPU进行最终的呈现
这样一个网页也就被渲染出来了。
四、渲染的完整过程
- 渲染主线程对html进行解析,生成dom树和cssom树
- 根据dom树和cssom树计算样式,从而再生成layout树
- 根据layout树再进行分层计算,生成每一层的绘制指令,渲染主线程完成工作
- 合成线程根据分层信息对每一层进行分块处理
- 然后将分块进行光栅化,将每个块变成位图,优先处理视口中的块
- 最后再计算每个位图在屏幕中的位置,然后通过GPU渲染在屏幕上
五、常见的一些问题
1. 什么是重排(reflow)?
重排(reflow)指的是当网页中的元素发生变化,影响到元素的布局和几何属性(例如尺寸、位置等)时,浏览器需要重新计算元素的布局,并重新渲染页面的过程。
从过程图可以看到,重排也就是又从解析html开始进行一遍渲染过程,所以频繁的重排是十分损耗性能的,开发时应该尽量避免。
如何减少重排?
- 批量修改 DOM: 尽量一次性修改多个 DOM 属性,而不是频繁地逐个修改。
- 使用 CSS3 动画: CSS3 动画通常由 GPU 渲染,不会触发 Reflow。
- 避免使用 table 布局: table 布局容易触发 Reflow。
- 减少使用 inline-style: 尽量使用外部样式表或内部样式表。
- 避免频繁读取 offsetWidth/offsetHeight 等属性: 这些属性会强制浏览器进行 Reflow。
2. 什么是重绘(repaint)?
重绘 (Repaint) 指的是当网页中的元素样式发生变化,但不影响元素的布局和几何属性(例如尺寸、位置等)时,浏览器会重新绘制该元素,但不会重新计算布局。简单来说,就是元素的外观发生了变化,但它在页面中的位置和大小没有改变。
从过程图中可以看到,重绘是从样式计算这一步开始重新进行,对比重排少了一步解析html,所以频繁的重绘也是十分损耗性能的。
如何减少重绘?
- 使用 CSS3 动画: CSS3 动画通常由 GPU 渲染,可以减少重绘的发生。
- 避免频繁修改样式: 尽量一次性修改多个样式属性,而不是频繁地逐个修改。
- 合理使用 will-change 属性: will-change 属性可以提前告知浏览器元素将要发生的变化,从而进行优化,但过度使用可能会导致性能问题。
3. 为什么 transform 效率⾼?
transform的修改不触发布局阶段(Layout/Reflow)和绘制阶段(Paint) ,仅影响合成阶段(Composite)。