Skip to content
On this page

渲染原理

按照渲染的时间顺序,渲染流水线可分为如下几个子阶段:构建 DOM 树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成。在了解各个子任务前,需要理解现代浏览器的多进程架构。

浏览器架构

线程 vs 进程

一个进程就是一个程序的运行实例。详细解释就是,启动一个程序的时候,操作系统会为该程序创建一块内存,用来存放代码、运行中的数据和一个执行任务的主线程,我们把这样的一个运行环境叫进程。而线程是依附于进程的,而进程中使用多线程并行处理能提高运行效率。

进程和线程关系有下面4个特点

  • 进程重的任意一线程执行出错,会导致整个进程的奔溃
  • 线程之间可以共享进程的数据
  • 当一个进程关闭之后,操作系统会回收进程所占用的内存
  • 进程之间的内容相互隔离

单进程浏览器架构

单进程浏览器的所有功能模块都是运行在用一个进程里,这些模块包含了网络、插件、JavaScript 运行环境、渲染引擎和页面等。

该架构浏览器存在以下的问题

  • 不稳定。页面的代码或者插件的意外奔溃会导致整个浏览器的奔溃
  • 不流畅。所有任务都泡在一个线程中,相互阻塞
  • 不安全。恶意插件,获取OS权限等

目前多进程架构

目前chrome浏览器包括: 1个浏览器主进程,1个GPU进程,1个网络进程,多个渲染进程和多个插件进程。

不同进程的作用如下:

  • 浏览器进程。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能
  • 渲染进程。将html, css和js文件渲染成网页, 有排版引擎blink和JS引擎V8。默认情况下, chrome会为每个tab标签都创建一个渲染进程,从页面打开同站点页面会复用同一个渲染进程。
  • GPU进程。在页面过程渲染过程起到绘制位图的作用。
  • 网络进程。负责页面的网络资源加载。
  • 插件进程。负责插件进程。

多进程架构可以很好的解决早期浏览器的3个缺点,但是存在更高资源占用,更复杂的体系架构的问题

面向未来的服务架构

原来的各种模块会被重构成独立的服务(Service),每个服务(Service)都可以在独立的进程中运行,访问服务(Service)必须使用定义好的接口,通过 IPC 来通信,从而构建一个更内聚、松耦合、易于维护和扩展的系统,更好实现 Chrome 简单、稳定、高速、安全的目标。

同时 Chrome 还提供灵活的弹性架构,Chrome 会将很多服务整合到一个进程中,从而节省内存占用。

构建DOM

因为浏览器无法直接理解和使用html文件,需要将html转为节点树结构--DOM树。DOM树的创建是通过HTML解析器来完成的,大概流程如下:

  • 通过分词器将字节流转为Token,分为tag token和文本token
  • 在创建Node节点过程中利用栈来处理,遇到start tag和文本入栈创建对应的dom节点,遇到end tag会匹配栈顶的start tag,匹配成功start tag出栈,表示该节点创建成功。

样式计算

  1. 把CSS文件内容转为浏览器理解的结构,CSSOM
  2. 计算样式属性值,比如rem转为px, color转为rbg表示等
  3. 利用CSS的继承和层叠特性计算DOM节点样式

布局阶段

根据DOM树和CSSOM计算出布局信息,构建一个布局树,也称渲染树。布局树只包含可见的节点信息:

分层

浏览器为了更好的实现复杂的3D变换,页面滚动,或者z-index排序,需要为特性的节点生成专用的图层,并最后生成一颗图层树。

并不是所有的节点都会生成图层,大概规则如下

  • 拥有层叠上下文属性的元素会被提升到单独一层,比如z-index, opacity,position等
  • 需要剪裁的地方。比如div里面的文本溢出裁剪
  • 如果一个节点没有对应的层,那么这个节点就从属于父节点的层

图层绘制

在得到图层树后,渲染引擎会把一个图层的绘制拆分成很多小的绘制指令,然后在将这些指令按顺序组成一个绘制列表:

光栅化

渲染引擎在生成好绘制指令列表后会交给合成线程处理,合成线程会对图层进行分块,优先处理在视口将要展示的图块。对图块进行光栅化处理,也就是生成位图,这个过程可以利用GPU进程进行加速处理。

合成和显示

一旦所有图块都进行完光栅化后,合成线程就会发送一个绘制图块的命令 DrawQuad 给浏览器主进程,然后浏览器进程将页面内容绘制在内存中,最后将内存显示在屏幕。

总结

用一张图来总结整个渲染进程

可见浏览器每一帧的图片生成还是挺复杂的,所以要尽量减少重排和重绘的操作,css动画要进行加速处理,直接进入合成阶段即可。