浏览器基本原理初探(一)—— 渲染原理

338 阅读4分钟

在聊浏览器基本原理之前,我们先简单说一下两个重要概念——进程和线程。

进程(Process):操作系统进行资源分配和调度的基本单位,可以申请和拥有计算机资源,进程是程序的基本执行实体。对于单进程应用来说,一个应用就是一个进程,对于多进程来说,一个应用可以拥有多个进程,可以获得更多的计算机资源

线程(Thread):操作系统中进行运算调度的最小单位,一个进程可以并发多个线程,每条线程并行执行不同的任务

现代的浏览器基本都是多进程架构浏览器,我们以Chrome为例,基本进程架构如下

image.png

  • 浏览器进程:地址栏,书签,前进,后退等功能以及整体协调其他进程工作;
  • 网络进程:发起和接收网络请求;
  • 渲染进程:解析网页内容;
  • 缓存进程:读写浏览器缓存;
  • GPU进程:浏览器页面渲染;
  • 插件进程:控制浏览器插件,如flash等,ps:并不是chrome市场安装的浏览器扩展插件 各进程运行的时候,又并发出多个线程执行,各进程之间通过ipc(Inter Process Communication)通信管道进行通信

渲染流程

当我们在浏览器地址栏输入一个地址后,浏览器向服务器发起http请求,服务器响应请求,并返回html文本内容,浏览器进程拿到html,交给渲染进程开始渲染页面

image.png

  1. 渲染进程(Render Process)分配主线程(Main Thread)解析html生成DOM Tree;
  2. 当解析过程中遇到link,script,img等标签时,通知网络进程发起资源请求,并把网络请求任务放在一个Task Queue(任务队列)中。如遇到script标签,主线程停止解析dom(步骤一暂停),直到js资源加载并执行完成(步骤一继续)ps: css资源和图片资源不会阻塞html解析;
  3. 主线程完成html文件解析后,遍历任务队列,拿到已完成请求的css资源开始解析生成CSSOM;
  4. 合成阶段:把 DOM Tree 和 CSSOM 进行合并生成 Render Tree(渲染树);
  5. 布局阶段(Layout Tree):根据 Render Tree 计算 DOM 元素在设备视口(viewport)中的位置;
  6. 绘制阶段:主线程遍历布局树创建绘制记录表(Paint Record),记录各元素绘制的先后顺序,绘制图层(Layer);
  7. 栅格化阶段:把图层(Composite Layers)合并成一个合成器帧通过IPC交给GPU进程绘制页面(由合成器线程和栅格线程完成,不占用主线程);
DOM Tree 和 Render Tree 是不完全一样的:
  1. display:none 的元素存在与DOM树,但不会出现在Render Tree中;
  2. 伪元素虽然在 DOM 树上不可见,但是在布局树上是可见的;
  3. 布局树决定了页面长什么样;

回流和重绘

回流:也叫重排,根据render tree计算dom元素在设备视口(viewport)中的位置

重绘:根据render tree和回流产生的元素位置信息,计算元素的具体样式

引起浏览器发生回流和重绘:

  1. dom元素样式的改变只会发生重绘(visibility,color,background,outline等);
  2. dom元素大小或者位置发生改变时,新增或删除元素时才会引起回流(width,height,position,margin),触发重新布局;
  3. 回流一定触发重绘,重绘不一定回流

动画卡顿

我们知道,当页面以每秒60帧的刷新率刷新页面时,才不会让用户感觉到顿挫。实际开发中,我们经常写一些页面动画,为什么我们写的动画往往会感觉很挫,有顿挫感呢?这里我们也简单聊一下动画卡顿的原因吧

卡顿原因

由于浏览器是单线程执行,运行过程中会出现绘制和js执行抢占主线程的情况。假如在一个绘制时间帧内绘制操作执行完成并且还有剩余时间,这时主线程就会被分配给js执行,如果js执行时间过长,就会导致在下一个绘制时间帧开始前没法及时归还主线程使用权,从而导致下一帧动画没有按时渲染,就会出现动画卡顿现象

解决方案
  • 方案一:requestAnimationFrame传入回调函数,浏览器将在每次开始下一次重绘前执行该回调函数。相当于把js代码分割成很多小块,插入到两个动画帧周期中间,每次绘制操作完成后执行js代码,下一个绘制周期开始时释放主线程继续绘制...
  • 方案二:动画效果尽量采用transform实现,因为transform不会经过布局,不占用主线程,也就不存在和js抢占主线程执行的问题