浏览器工作原理(三):渲染过程

之前提到过,凡是需要渲染主线程执行的任务,都要进入事件循环排队。当网络进程将 html 文档下载到本地后,会创建一个渲染任务进入事件循环,然后由渲染主线程开始渲染工作。

在浏览器的众多进程线程中,渲染核心相关的如下图。

整个渲染过程如下图所示。

下面我们介绍这些步骤。

解析 html 和 css

渲染的第一步就是将 html 文本解析成树结构,称为文档对象模型。

在浏览器中,通过 document 对象可以访问到 dom 树的各个节点。

同样地,css 代码也会被解析成树结构,称为 CSSOM。

StyleSheetList 是根节点,下面是多个 CSSStyleSheet 对象,每个 CSSStyleSheet 对应代码中的内部或外部样式表。

javascript 复制代码
document.styleSheets // 根节点 StyleSheetList

我们可以用 JS 操作 CSSOM,从而修改或动态添加样式。

javascript 复制代码
document.styleSheets[0].addRule('div', 'border: 1px solid #000') // 选择器和样式声明

值得一提的是,为了提高效率,浏览器会启动一个预解析线程快速找到 html 中的 css 链接并下载解析,如果是 JS 文件,则必须停下等待其下载且执行完毕,这是因为 css 解析不影响 html 解析而 JS 会影响。

样式计算

DOM 和 CSSOM 都准备好后,主线程的下一步是样式计算。其实就是计算每个 dom 节点的最终样式,这个最终样式会附着于该 dom 节点上,称为 computed style。

在 chrome 中我们可以查看一个节点的 computed style,也可以用 getComputed 方法获取。

javascript 复制代码
getComputedStyle(document.body)

布局 Layout

现在我们有了一棵包含 computed style 的 dom 树,下一步是计算布局 ( layout )。

layout 过程就是根据 dom 树中每个节点的 computed style,计算节点的尺寸和位置。layout 的计算过程和视觉格式化模型息息相关。

dom 树和 layout 树不是一一对应的。比如 display: none 的节点不存在于 layout 树中;伪元素不存在于 dom 树,但会存在于 layout 树。

分层 Layer

分层类似图片编辑里的图层概念,主线程将它认为"经常一起变化"的节点生成一个新图层。

将哪些节点作为一个分层是浏览器决定的,我们也可以给浏览器一些提示,从而影响它的分层决策。跟堆叠上下文相关的 css 属性,比如 z-index、opacity、transform、will-change 会影响浏览器的分层结果。其中影响较大的是 will-change 属性。

css 复制代码
.div {
  will-change: transform; /* transform 属性会经常改变 */
}

以上属性提示浏览器,transform 属性会经常改变,浏览器会决定是否将这个 div 单独作为一层。

分层可以提高重新渲染的效率,但是分层需要占用较多内存,因此浏览器会对层数做取舍,兼顾内存和效率。

绘制 Paint

所谓绘制,就是为每个 layer 生成一系列绘制指令。绘制指令类似于 canvas 中的操作。事实上,canvas 就是浏览器将绘制功能暴露给开发者而来,也就是说,canvas 操作的就是绘制过程。

以下是绘制指令的一种方式。

makefile 复制代码
Draw Rect
pos: x, y, w, h
color: red

到目前为止我们介绍的是渲染主线程的操作,目前屏幕上仍未得到像素点,只有一系列绘制指令,这些指令会交给合成线程处理。

分块 Tiling

GPU 擅长并行执行,为提高光栅化和画的效率,我们需要对 layer 进行分块,也就是将每个 layer 层分成多个小区域,这样有利于并行操作。

分块在合成线程中完成,它是渲染进程的线程。合成线程会在线程池中获取多个分块器(线程),它们同时工作,提升分块效率。

关于线程池,就是里面有多个创建好的线程,使用者可以申请其中空闲的线程使用,如果没有空闲线程,则排队等待。这是为了减少频繁创建回收线程的开销,同时控制线程的数量,避免创建太多线程。

光栅化 Raster

光栅化就是将每个 tile 变成位图,且为了提高体验,浏览器会优先处理靠近视口的 tile。

光栅化是在浏览器的 GPU 进程中执行的,GPU 进程内也会开启多个线程执行。

画 Draw

光栅化完成后,合成线程计算每个位图在屏幕上的位置(quad 信息),同时进行 css 的 transform 转换。这些信息会交给 GPU 进程,然后由 GPU 进程进行系统调用,从而交给显卡,最终呈现在屏幕上。

之所以要转交给 GPU 进程,是因为合成线程所在的渲染进程是在沙盒中运行的(安全策略),无法进行系统调用。

参考

How does browser work step by step [latest] --- rendering phase (part 3)

相关推荐
Martin -Tang25 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发26 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
我不当帕鲁谁当帕鲁1 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂1 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成4 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽4 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新5 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html
秦jh_6 小时前
【Linux】多线程(概念,控制)
linux·运维·前端