x-note
  • Introduction
  • JavaScript
    • JavaScript 作用域链
    • JavaScript 数据结构与类型
    • JavaScript 原型
    • JavaScript this 关键字
    • JavaScript 函数
    • JavaScript delete 运算符
    • JavaScript 内存管理与垃圾回收
    • JavaScript 严格模式与混乱模式
    • JavaScript 数字精度丢失
    • JavaScript 并发模型
    • 利用原型链实现继承
  • ECMAScript
    • ECMAScript 6 变量及常量的声明
    • ECMAScript 6 变量的解构赋值
    • ECMAScript 6 Promise 对象
    • ECMAScript 6 Symbol
    • ECMAScript 6 Proxy
    • ECMAScript 6 Reflect
    • ECMAScript 6 new.target
    • ECMAScript 6 Set 和 WeakSet
    • ECMAScript 6 Map 和 WeakMap
    • ECMAScript 6 Iterator
    • ECMAScript 6 Generator
    • ECMAScript 6 class
    • ECMAScript 7
    • ECMAScript 8 async 函数
    • ECMAScript 8 内存共享与原子性
    • ECMAScript 8 Others
    • ECMAScript 2018
    • ECMAScript 2019
  • CSS
    • CSS 块格式化上下文(BFC)
    • CSS 盒模型
    • CSS 外边距合并
    • CSS Float
    • CSS Position
    • CSS Border-Image
    • CSS BEM
    • CSS 表布局详解
    • 页面布局之单列布局
    • 页面布局之多列布局
  • React
    • React 组件的生命周期
    • React 虚拟 DOM
    • React Reconciliation
    • React Diff 算法核心
    • React Fiber
    • React Scheduling
    • React Context API
    • React Refs
    • React HMR
    • React Hook
  • VUE
    • VUE 响应式系统
    • VUE 渲染机制
    • 关于 Vue 的思考
  • Webpack
    • Webpack 基本概念
    • Webpack HMR
  • Babel
    • @babel/preset-env
  • WEB
    • WEB 基础知识及概念
      • 屏幕测量单位
      • 重绘与重排
      • 前端模块化系统
      • WEB 客户端存储
      • 浏览器的渲染过程
    • WEB 性能优化
      • WEB 性能指标
      • WEB 图片优化
      • 懒加载资源
    • WEB 安全
      • XSS
      • XSRF
      • 点击劫持
      • 同源策略(Same Origin Policy,SOP)
    • WEB 解决方案
      • webp 兼容方案
      • WEB 拖拽实现方案
    • WEB SEO
  • Git
    • Git 工作流
    • Git 内部原理
  • 传输协议
    • UDP
      • UDP 基本概念
    • TCP
      • TCP 基本概念
    • HTTP
      • HTTP 基础
      • HTTP 缓存
      • HTTP-2
      • HTTP-3
      • HTTPS
      • 自定义 HTTPS 证书
  • Protocol Buffers
    • Protocol Buffers 基础
  • gRPC
    • gRPC 简介
    • gRPC 基础概念
    • GRPC with GraphQL and TypeScript
  • 正则表达式
    • 正则表达式基础
    • 正则表达式的悲观回溯
  • 基础算法
    • 冒泡排序
    • 插入排序
    • 选择排序
    • 快速排序
    • 归并排序
    • 希尔排序
    • 堆排序
    • 桶排序
    • 计数排序
    • 基数排序
    • 二叉树的遍历
    • 动态规划
    • 回溯
  • 压缩算法
    • HPACK
    • QPACK
  • 设计模式
    • DDD
      • 模型元素的模式
    • 常见设计模式
      • 工厂方法
      • 抽象工厂
      • 构造器
      • 原型
      • 单例模式
      • 适配器模式
      • 桥接模式
      • 组合模式
      • 外观模式
      • 享元模式
      • 代理模式
      • 责任链模式
      • 命令模式
      • 迭代器模式
      • 中介者模式
      • 备忘录模式
      • 观察者模式
      • 状态模式
      • 策略模式
      • 模版方法模式
      • 访问者模式
      • 依赖注入
    • MVC
    • MVP
    • MVVM
  • 颜色空间
    • LCH
由 GitBook 提供支持
在本页
  • 浏览器中的进程
  • 浏览器引擎种类
  • 渲染进程详细
  • 渲染进程的线程类别
  • JS 引擎类别
  • 渲染进程工作流程
  • 构建文档对象模型树
  • 构建样式对象模型树
  • 构建渲染对象树
  • 布局(Paint)
  • 绘制(Paint)
  • 参考资料
在GitHub上编辑
  1. WEB
  2. WEB 基础知识及概念

浏览器的渲染过程

浏览器中的进程

目前浏览器多为多进程架构,页面的加载、渲染和交互是由多个进程配合完成。

  • 浏览器主进程。

    • 负责界面显示

    • 用户交互

    • 子进程管理

    • 同时提供存储等功能

  • 渲染进程

    • 将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页

    • 渲染引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中

    • 出于安全考虑,渲染进程都是运行在沙箱模式下,无法访问系统资源

    • 出于性能 Chrome 会为每个 Tab 标签创建一个渲染进程,其它内核的浏览器只有一个渲染进程

  • 网络进程

    • 主要负责页面的网络资源加载。

    • 之前是作为一个模块运行在浏览器主进程里面的,直至最近才独立出来,成为一个单独的进程。

  • GPU 进程

    • GPU 的使用初衷是为了实现 3D CSS 的效果

    • 随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求

  • 插件进程

    • 负责插件的运行

    • 出于鲁棒性考虑,插件进程隔离插件执行保证插件异常时页面不受影响

  • 其它进程

浏览器引擎种类

浏览器引擎(Browser Engine),或者称为浏览器排版引擎(Browser Layout Engine),目前主流有以下几种。

浏览器排版引擎和浏览器渲染引擎不是一回事,两者紧密相关互相耦合

IE 已死,Edge、Opera 都已改为采用 Blink. 而 Blink 是 Webkit 的变种,其对自身的定义为“渲染引擎(rendering engine)”。

大体上浏览器引擎仅剩 Webkit 和 Gecko 两大分支

渲染进程详细

渲染进程的线程类别

导航过程完成之后,浏览器进程把数据交给了渲染进程,渲染进程负责 Tab 选项卡内的所有事情,核心目的就是将 HTML/CSS/JavaScript 代码,转化为用户可进行交互的 Web 页面。

在该过程中渲染进程会开启多个线程协作完成,主要的线程以及作用如下:

  • GUI 渲染线程

  • JS 引擎线程

  • 事件触发线程

  • 定时器触发线程

  • 异步 http 请求线程

GUI 渲染线程与 JS 引擎线程是相互排斥的。 因为 JS 引擎线程在执行的过程中可能会发生回流和重绘,所以 GUI 渲染线程执行时候,JS 引擎线程会被挂起,等待 GUI 渲染线程执行完毕之后。 同理,当 JS 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存起来等到 JS 引擎空闲时立即被执行。 所以如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染阻塞。

JS 引擎类别

  • SpiderMonkey

    • Firefox

  • V8

    • Chromium

    • Node

    • Opera

  • JavaScriptCore

    • Safari

渲染进程工作流程

渲染引擎的基本流程:

  1. 解析文档

    • 构建文档对象模型树

    • 构建样式对象模型树

    • 脚本异步加载

  2. 构建渲染对象树

  3. 布局/排版(Layout/flow)。

  4. 绘制(Paint)

    1. 光栅化(Rasterization)

    2. 合并(Composite)

这个过程是逐步完成的, 为了更好的用户体验,渲染引擎将会尽可能早地将内容呈现到屏幕上, 并不会等到所有 HTML 都解析完成之后再去构建和布局渲染树, 它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。

另外,不同渲染引擎也有细微差异....

Gecko 将视觉格式化元素树称为“Frame Tree”,每个元素都是一个 Frame。 WebKit 使用术语 “Render Tree”,它由 “Render Objects” 组成

Webkit 工作流程:

Gecko 工作流程:

构建文档对象模型树

DOM 是文档对象模型 (Document Object Model) 的简称。 它是 HTML 文档的对象呈现方式,也是 HTML 元素与 JavaScript 等外部环境之间的接口。

DOM 与 HTML 标记之间几乎是一对一的关系。

创建解析器时,也会创建 Document 对象。 在树构建阶段,以 Document 为根部的 DOM 树将被修改,并且向其中添加元素。 标记生成器发出的每个节点都将由树构造函数进行处理。 对于每个词法单元,规范都会定义与其相关的 DOM 元素,并将为这个词元创建这些元素。 该元素随即会添加到 DOM 树以及开放元素的堆栈中。 此堆栈用于更正嵌套错误和未关闭的标记。 该算法也可描述为状态机。 这些状态称为“插入模式”。

构建样式对象模型树

渲染引擎无法直接理解 CSS 文件的内容,所以需要将其解析成渲染引擎能够理解的结构,即 styleSheets。

WebKit 使用 Flex 和 Bison 解析器生成器,根据 CSS 语法文件自动创建解析器。 Bison 会创建一个自下而上的移位归约解析器。

Gecko 使用手动编写的自上而下解析器。

CSSOM(CSS Object Model)其作用为:

  • 提供给 JavaScript 操作样式表的能力

  • 图层树的合成提供基础的样式信息。

构建渲染对象树

在 DOM 树构建期间,浏览器会构建另一个树,即渲染树。 该树状图中的视觉元素按照显示顺序排列。 它是文档的可视化表示。 这种树旨在确保按正确的顺序绘制内容。

渲染程序与 DOM 元素相对应,但并非一对一关系。 非可视化 DOM 元素不会插入渲染树中。例如“head”元素。 此外,display:none 的元素也不会显示在树中

构建渲染树需要计算每个渲染对象的视觉属性。 这是通过计算每个元素的样式属性来完成的。 样式包括不同来源的样式表、内嵌样式元素和 HTML 中的视觉属性

WebKit 节点会引用样式对象 (RenderStyle)。 在某些情况下,这些对象可以由节点共享。

为了简化样式计算,Gecko 还采用了另外两种树:规则树(rule tree)和样式上下文树(style context tree)。 WebKit 也有样式对象,但它们并不存储在样式上下文树这样的树中,只有 DOM 节点指向其相关样式。

示例:

<!-- HTML -->
<html>
  <body>
    <div class="err" id="div1">
      <p>
        this is a <span class="big"> big error </span>
        this is also a
        <span class="big"> very big error</span> error
      </p>
    </div>
    <div class="err" id="div2">another error</div>
  </body>
</html>
/* CSS */
div {
  margin: 5px;
  color: black;
}
.err {
  color: red;
}
.big {
  margin-top: 3px;
}
div span {
  margin-bottom: 4px;
}
#div1 {
  color: blue;
}
#div2 {
  color: green;
}

WebKit 使用一个标记来标记是否所有顶级样式表(包括 @imports)均已加载完毕。如果在添加样式时样式未完全加载,则会使用占位符并在文档中对其进行标记,并在样式表加载完毕后重新计算占位符。

布局(Paint)

渲染引擎在创建完成并添加到呈现树时,并不具有位置和大小。 计算这些值的过程称为布局或自动重排。 最终会创建图层树(Layer Tree)它将确定每个节点在屏幕上的确切坐标。

布局阶段会从图层树的根节点开始遍历,然后确定每个元素盒子在页面中的确切大小与坐标位置的绝对像素值。布局流程:

  1. 父 renderer 会自行确定宽度。

  2. 父级会处理子级,并执行以下操作

    1. 放置子 renderer。(设置其 x 和 y)

    2. 根据需要调用子布局

  3. 父级使用子级的累计高度以及外边距和内边距的高度来设置自己的高度, 此值将由父级渲染器的父级使用

  4. 将其 dirty 设置为 false

通常情况下,并不是图层树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。

HTML 使用基于流的布局模型。 “流中”后期的元素通常不会影响更早“流中”元素的几何形状,因此布局可以按从左到右、从上到下的顺序遍历文档。 除了 <Table /> 外,只需一次遍历即可计算出几何图形。

如果渲染程序发生更改或添加,则会将其自身及其子项标记为“dirty”需要布局。

在整个渲染树上触发布局,这就是“全局”布局。例如:

  • 屏幕大小调整。

  • 影响所有渲染程序的全局样式更改,例如字体大小更改

全局布局通常会被同步触发。

当渲染程序处于 dirty 时,会(异步)触发增量布局。 例如,当额外内容来自网络并添加到 DOM 树之后,新的渲染程序才附加到渲染树。

绘制(Paint)

构建完图层树(Layer Tree)之后,渲染引擎会遍历渲染树,并调用渲染程序的“paint()”方法,并绘制每个节点

与布局一样,绘制也可以是全局(绘制整个树)或增量绘制。

绘制顺序:

  1. 背景颜色

  2. 背景图片

  3. 边框

  4. 子节点内容

  5. Outline

绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。 当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程

显示列表

Gecko 遍历渲染树,为绘制的矩形构建一个显示列表。 重新绘制时,只需遍历一次树。 Gecko 还对处理过程进行了优化,如完全位于其他不透明元素下方的元素

栅格化(Rasterization)

WebKit 会将旧的矩形保存为位图,然后只绘制新旧矩形之间的增量。

所谓栅格化,是指将图块(tile)转换为位图(这些图块的大小通常是 256x256 或者 512x512)。 而图块是栅格化执行的最小单位。

合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。

合并(Composite)

在光栅化完成之后,每一个图块都对应一张图片,合成线程会将这些图片合成为“一张”图片,将其绘制到内存中,最后再将内存显示在屏幕上。

参考资料

上一页WEB 客户端存储下一页WEB 性能优化

最后更新于1年前

。

。

Webkit (Safari)
Blink (Chromium)
Gecko (Firefox)
Presto (Opera)
Trident (Internet Explorer)
何时创建层叠上下文(Stacking Context),参考 MDN
Browser Engine and Types of Browser Engines
How browsers work
浏览器渲染流程和性能优化
渲染进程的内部机制
Webkit Workflow
Gecko Workflow
Rule Tree