浏览器的渲染原理

浏览器渲染流程

当浏览器的网络引擎接收到了网络请求返回的HTML文档时,它会将其解析成一系列的渲染任务,并将这些任务放入渲染主线程的消息队列中。在事件循环机制的作用下,渲染主线程会按照队列的顺序依次处理这些渲染任务。

当渲染主线程开始渲染操作时,它会从消息队列中取出相应的渲染任务,并根据这些任务的要求对页面进行相应的渲染。在渲染过程中,渲染主线程会根据HTML文档中的元素和样式计算出相应的绘制指令,并将这些指令发送给GPU进行绘制。

通过这样的流程,最终将精美的网页呈现在用户眼前。

渲染过程阶段

渲染过程包含多个阶段,这些阶段相互衔接并形成一条流水线。具体来说,这些阶段包括:解析HTML、样式计算、布局、分层、绘制、分块、光栅化和作画。

在每个阶段,都会有一个输入和一个输出。输入是上一个阶段的输出,而输出将成为下一个阶段的输入。这种流水线式的结构确保了渲染过程的连续性和一致性。

解析HTML

将HTML解析成DOM树(Document Object Model)和cssom树(CSS Object Model)

将HTML字符串转换为document对象,方便数据的使用和js的访问

将样式表转换为styleSheetList对象,方便数据使用和js的访问

在样式表列表styleSheetList中,包含了多个不同种类的样式表。这些样式表各自拥有众多不同的样式规则,每个规则又包含一个或多个选择器,以及相应的样式style

  • 样式表类型分为: 行内样式、内部样式、外部样式、浏览器默认样式 、
  • js获取行内样式:dom.style
  • js获取所有类型样式表:document.styleSheet,通过addRule方法进行添加规则

HTML 解析过程中遇到 CSS 代码

为了提高效率,浏览器会启动一个预解析线程率先下载和解析css

当渲染主线程遇到一个链接(link)标签指向外部CSS文件时,它会立即开始下载该文件,而不会等待CSS文件完全下载和解析完成。这是因为下载和解析CSS文件的工作是在一个独立的预解析线程中进行的。因此,主线程在CSS文件尚未完全下载和解析的情况下不会等待,而是继续解析后续的HTML代码。这也就解释了为什么CSS不会阻塞HTML解析的根本原因。

HTML 解析过程中遇到 JS 代码

当渲染主线程遇到JS会停止一切行为,等待预解析线程下载完后才能执行

在主线程进行HTML解析的过程中,如果遇到脚本(script)标签,它会停止HTML解析,转而等待相关的JavaScript文件下载完成。一旦全局代码被解析并执行后,才会继续解析HTML。这是因为JavaScript的执行过程可能会修改当前的DOM树,因此,DOM树的生成必须暂停。这就是JavaScript能够阻塞HTML解析的根本原因。

样式计算

将HTML和CSSOM树解析后,主线程将遍历DOM树,为每个节点精确计算出最终的样式,这一过程被称为计算样式(Computed Style)。最终,主线程会将计算后的样式应用于DOM树,从而得到最终的渲染树。

布局

在CSS中,将元素的display属性设置为none会导致该元素在DOM树中隐藏。相应地,该元素也不会出现在布局树中,因为它不会影响页面的布局和渲染。

在CSS中,使用::before伪元素会在DOM树中创建一个额外的节点,该节点会在布局树中生成一个额外的分支。这意味着它会在页面布局和渲染时占据一定的空间。

  • 内容必须在行盒中
  • 行盒和快盒不能相邻

注意:

  1. 行内元素和块级元素是指html的标签
  2. 行盒和块盒是指布局的样式

在布局阶段,主线程会逐一浏览DOM树的每个节点,并计算其几何属性,如宽度、高度以及相对于其包含块的位置。在大部分情况下,DOM树和布局树之间的关系并非一一对应,这意味着每个DOM节点可能对应于布局树中的多个节点,反之亦然。

分层

分层的优势在于,当某一层在未来发生改变时,只需对该层进行后续处理,从而有效提升工作效率。

滚动条、堆叠上下文、变换(transform)和透明度(opacity)等样式都会在一定程度上对分层的结果产生影响。然而,我们可以通过使用will-change属性来更大程度地影响分层结果,以进一步优化性能。

css的will-change属性和作用

CSS的will-change属性是一个用于通知浏览器某个元素即将发生变化的属性。它可以被应用到任何元素上,用于提前告知浏览器该元素将要有哪些属性进行改变,从而优化渲染性能。

通过在元素上设置will-change属性,开发者可以明确指示浏览器对该元素进行优化处理。这样一来,浏览器可以提前分配资源和准备工作,以便在实际改变发生之前进行相应的合成操作。这样做有助于避免不必要的重绘和重排,提高页面的响应速度和动画的流畅度。

will-change属性可以接受多个属性值,表示将要改变的属性。例如,.element{will-change:transform,opacity;}在上述例子中,我们告诉浏览器该元素将要发生的变化是transform和opacity属性。浏览器会根据这个信息进行相应的预处理,并为元素分配更高效的渲染方式,如创建一个独立的图层或使用GPU加速等。

尽管will-change属性可以带来性能优化,但滥用它可能会导致负面影响。因为过多地使用will-change可能会导致浏览器过度优化,导致资源的浪费。因此,应仅在确实需要进行频繁改变的元素上使用will-change,以避免不必要的复杂性和性能问题。

总结起来,will-change是一个用于告知浏览器某个元素即将发生变化的CSS属性,它可以帮助优化渲染性能,提高页面的响应速度和动画的流畅度。

绘制

对分层后的布局进行绘制,并为每一层生成如何绘制的指令,最终形成一个包含所有绘制指令的集合。这样可以在需要时根据该指令集对页面进行绘制并呈现最终结果。

当我们浏览器的渲染过程进行到绘制阶段后,后续步骤将由其他线程负责执行。这样,主线程可以专注于其他任务,提高浏览器的整体性能。

分块

将每一层进一步分割为多个小的区域,这样可以更有效地利用浏览器的渲染资源,提高绘制效率。

主线程将每个图层的绘制指令和信息提交给合成线程,剩余的工作将由合成线程全权负责。

合成线程首先会对每个图层进行分块处理,将其细分为更多的小区域。

为了提高效率,它会从线程池中获取多个线程来共同完成这个分块过程。

光栅化

每个小块都会被转化为位图,而在这些位图中,当前视口内的位图会率先进行光栅化处理。随后,其他的块也会陆续进行光栅化处理。

合成线程会将各个块的信息发送给 GPU 进程,以利用其高效的处理能力来迅速完成光栅化操作。GPU 进程将启动多个线程来并行处理这些块,并且会优先处理靠近视口的区域。最终,光栅化的结果将以一块一块的位图形式呈现出来。

作画

合成线程在获取每个层、每个块的位图后,会生成一系列的"指引"(quad)信息。这些指引会详细标识出每个位图应该被绘制到屏幕的哪个位置,同时也会考虑到如旋转、缩放等可能的变形。

由于这些变形操作都在合成线程中完成,与主渲染线程完全独立,因此transform属性的使用效率能够得到大幅提升。

最后,合成线程将这些quad提交给GPU进程。GPU进程则会通过系统调用来将这些指令提交给底层的GPU硬件,最终完成最终的屏幕成像。

最终流程

思考

  • 什么是reflow

reflow 的本质就是重新计算页面元素的位置和大小,即重新计算 layout 树。当对页面元素进行修改操作时,这些操作会影响到页面的布局,因此需要重新计算 layout 树以反映这些更改。

为了避免连续的多次操作导致 layout 树的反复计算,浏览器会将这些操作合并在一起,然后在 JS 代码全部执行完毕后再进行一次性的计算和渲染。因此,当 JS 修改了页面元素的属性时,这些改动造成的 reflow 是异步完成的。

同样地,由于 reflow 是异步的,当 JS 代码在获取页面元素的布局属性时,可能会获取到旧的布局信息而不是最新的布局信息。

最终,浏览器在权衡性能和准确性后,决定在获取属性时立即进行 reflow。

  • 什么是repaint

repaint 的本质是指浏览器根据修改后的分层信息重新计算并执行绘制指令。当页面的可见样式发生改变时,这些改变需要重新计算并反映在页面的视觉表现上,这就会引发 repaint。

同时,元素的布局信息也属于可见样式的一部分,因此当布局发生变化时,一定会引发 reflow,从而触发 repaint。

  • 为什么 transform 效率⾼

因为transform属性只会影响渲染流程的最后一个"绘制"阶段,而不会对布局或绘制指令产生任何影响。

由于绘制阶段是在合成线程中进行的,因此transform属性的变化几乎不会对渲染主线程产生影响。相反,无论渲染主线程的工作负荷多么繁重,都不会影响到transform属性的变化。

相关推荐
贩卖纯净水.1 小时前
webpack其余配置
前端·webpack·node.js
码上奶茶1 小时前
HTML 列表、表格、表单
前端·html·表格·标签·列表·文本·表单
抹茶san1 小时前
和 Trae 一起开发可视化拖拽编辑项目(1) :迈出第一步
前端·trae
风吹头皮凉1 小时前
vue实现气泡词云图
前端·javascript·vue.js
南玖i1 小时前
vue3 + ant 实现 tree默认展开,筛选对应数据打开,简单~直接cv
开发语言·前端·javascript
小钻风33662 小时前
深入浅出掌握 Axios(持续更新)
前端·javascript·axios
萌萌哒草头将军2 小时前
🚀🚀🚀尤雨溪推荐的这个库你一定要知道!轻量⚡️,优雅!
前端·vue.js·react.js
三门2 小时前
docker安装mysql8.0.20过程
前端
BillKu3 小时前
Vue3 + Vite 中使用 Lodash-es 的防抖 debounce 详解
前端·javascript·vue.js