[1.1W字]写给女朋友的秘籍-浏览器工做原理(渲染流程)篇

前言

想要成为一名合格的前端工程师,掌握相关浏览器的工做原理是必备的,这样子才会有一个完整知识体系,要是能参透浏览器的工做原理,你就能解决80%的前端难题css

其余文章:html

这篇文章准备梳理一下渲染流程,也就是浏览器是怎么把HTML,CSS,JS,图片等资源最后显示漂亮的页面。前端

仍是那句话,了解浏览器是如何工做的,能让你站在更高维度去理解前端html5

但愿经过这篇文章,可以让你从新认识浏览器,并把JavaScript,网络,页面渲染,浏览器安全等知识串联起来,从而让你对整个前端体系有全新的认识。web

这篇主要是梳理了渲染流程中几个重要的步骤,以及从中有哪些优化的点,怎么样避免和减小重绘、重排,对优化性能上有必定的帮助。面试

读完这一期内容,你将收获算法

  • 前端性能优化的底层逻辑;
  • 浏览器页面渲染的核心流程
  • JavaScript 运行机制解析
  • 浏览器网络及安全机制解析

小声说:欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程👊chrome


若是喜欢的话能够点赞/关注,支持一下,但愿你们能够看完本文有所收获

回顾上一期

讲浏览器中渲染流程,先把上一篇所梳理的内容大概回顾一下segmentfault

上一篇文章:[1.2W字👍]写给女朋友的秘籍-浏览器工做原理(上)篇api

浏览器架构(Chrome为主)

主要梳理如下知识点:

  • 梳理单线程与多线程概念,进程与线程区别
  • 以Chrome浏览器为主,梳理了Chrome浏览器发展史,从单进程浏览器到多进程浏览器
  • 单进程架构到多进程架构趋势,发展趋势的优缺点
  • 如今多数浏览器多进程架构:主进程 渲染进程 插件进程 GPU进程 网络进程

Http请求

主要梳理如下知识点:

  • Http请求的总体流程,大体分为八个阶段

  • 八个阶段归纳为: 构建请求 查找缓存 准备 IP 和端口 等待 TCP 队列 创建 TCP 链接 发起 HTTP 请求 服务器处理请求 服务器返回请求和断开链接

  • 每一个阶段大体的工做原理梳理了一下

  • 浏览器缓存顺带提起了一下(面试常考) 性能优化一部分

导航流程:输入url到页面展现

主要梳理知识点:

  • 总体流程,梳理这个过程当中最为主要的三个进程,浏览器进程 网络进程 渲染进程,它们各自的职责以及三者之间的通讯
  • 尝试去分析每一个阶段,细节不说起了。

渲染进程接受到CommitNavigation消息以后,便开始与网络进程创建数据管道提及,此时渲染进程开始干活了

那么才有了咱们接下来要梳理的内容,渲染进程如何工做的呢

渲染流程

首先要了解的概念:

  • 渲染引擎:它是浏览器最核心的部分是 “Rendering Engine”,不过咱们通常习惯将之称为 “浏览器内核”

  • 渲染引擎主要包括的线程:

  • 各个线程主要职责

    • GUI渲染线程:GUI 渲染线程负责渲染浏览器界面,解析 HTML,CSS,构建 DOM 树和 RenderObject 树,布局和绘制等。当界面须要重绘(Repaint)或因为某种操做引起回流(Reflow)时,该线程就会执行。
    • JavaScript引擎线程: JavaScript 引擎线程主要负责解析 JavaScript 脚本并运行相关代码。 JavaScript 引擎在一个Tab页(Renderer 进程)中不管何时都只有一个 JavaScript 线程在运行 JavaScript 程序。须要提起一点就是,GUI线程与JavaScript引擎线程是互斥的,这也是就是为何JavaScript操做时间过长,会形成页面渲染不连贯,致使页面出现阻塞的原理。
    • 事件触发线程:当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JavaScript 引擎的处理。 一般JavaScript引擎是单线程的,因此这些事件都会排队等待JS执行。
    • 定时器触发器: 咱们平常使用的setInterval 和 setTimeout 就在该线程中,缘由可能就是:因为JS引擎是单线程的,若是处于阻塞线程状态就会影响记时的准确,因此须要经过单独的线程来记时并触发响应的事件这样子更为合理。
    • Http请求线程: 在 XMLHttpRequest 在链接后是经过浏览器新开一个线程请求,这个线程就Http请求线程,它 将检测到状态变动时,若是设置有回调函数,异步线程就产生状态变动事件放到 JavaScript 引擎的处理队列中等待处理。

以上来自阿宝哥总结:你不知道的 Web Workers (上)[7.8K 字 | 多图预警]

有了上述的概念,对接下咱们讲渲染流水线会有所帮助

简略版的渲染机制

好久以前就把浏览器工做原理读完了,看了不少博客,文章,当时简简单单的梳理一些内容,以下👇

简略版渲染机制通常分为如下几个步骤

  1. 处理 HTML 并构建 DOM 树。
  2. 处理 CSS 构建 CSSOM 树。
  3. 将 DOM 与 CSSOM 合并成一个渲染树。
  4. 根据渲染树来布局,计算每一个节点的位置。
  5. 调用 GPU 绘制,合成图层,显示在屏幕上。

接下来大概就是这么说:

在构建 CSSOM 树时,会阻塞渲染,直至 CSSOM 树构建完成。而且构建 CSSOM 树是一个十分消耗性能的过程,因此应该尽可能保证层级扁平,减小过分层叠,越是具体的 CSS 选择器,执行速度越慢。

当 HTML 解析到 script 标签时,会暂停构建 DOM,完成后才会从暂停的地方从新开始。也就是说,若是你想首屏渲染的越快,就越不该该在首屏就加载 JS 文件。而且 CSS 也会影响 JS 的执行,只有当解析完样式表才会执行 JS,因此也能够认为这种状况下,CSS 也会暂停构建 DOM。

说完这些,记下来就讲几个面试经常会提起的,会问你的知识点👇

Load 和 DOMContentLoaded 区别

Load 事件触发表明页面中的 DOM,CSS,JS,图片已经所有加载完毕。

DOMContentLoaded 事件触发表明初始的 HTML 被彻底加载和解析,不须要等待 CSS,JS,图片加载。

图层

通常来讲,能够把普通文档流当作一个图层。特定的属性能够生成一个新的图层。不一样的图层渲染互不影响,因此对于某些频繁须要渲染的建议单独生成一个新图层,提升性能。但也不能生成过多的图层,会引发副作用。

经过如下几个经常使用属性能够生成新图层

  • 3D 变换:translate3dtranslateZ
  • will-change
  • videoiframe 标签
  • 经过动画实现的 opacity 动画转换
  • position: fixed

重绘(Repaint)和回流(Reflow)

重绘和回流是渲染步骤中的一小节,可是这两个步骤对于性能影响很大。

  • 重绘是当节点须要更改外观而不会影响布局的,好比改变 color 就叫称为重绘
  • 回流是布局或者几何属性须要改变就称为回流。

回流一定会发生重绘,重绘不必定会引起回流。回流所需的成本比重绘高的多,改变深层次的节点极可能致使父节点的一系列回流。

因此如下几个动做可能会致使性能问题:

  • 改变 window 大小
  • 改变字体
  • 添加或删除样式
  • 文字改变
  • 定位或者浮动
  • 盒模型

不少人不知道的是,重绘和回流其实和 Event loop 有关。

  1. 当 Event loop 执行完 Microtasks 后,会判断 document 是否须要更新。由于浏览器是 60Hz 的刷新率,每 16ms 才会更新一次。
  2. 而后判断是否有 resize 或者 scroll ,有的话会去触发事件,因此 resizescroll 事件也是至少 16ms 才会触发一次,而且自带节流功能。
  3. 判断是否触发了 media query
  4. 更新动画而且发送事件
  5. 判断是否有全屏操做事件
  6. 执行 requestAnimationFrame 回调
  7. 执行 IntersectionObserver 回调,该方法用于判断元素是否可见,能够用于懒加载上,可是兼容性很差
  8. 更新界面
  9. 以上就是一帧中可能会作的事情。若是在一帧中有空闲时间,就会去执行 requestIdleCallback 回调。

以上内容来自于 HTML 文档

减小重绘和回流

  • 使用 translate 替代 top

    <div class="test"></div>
    <style>
    	.test {
    		position: absolute;
    		top: 10px;
    		width: 100px;
    		height: 100px;
    		background: red;
    	}
    </style>
    <script>
    	setTimeout(() => {
            // 引发回流
    		document.querySelector('.test').style.top = '100px'
    	}, 1000)
    </script>
    复制代码
  • 使用 visibility 替换 display: none ,由于前者只会引发重绘,后者会引起回流(改变了布局)

  • 把 DOM 离线后修改,好比:先把 DOM 给 display:none (有一次 Reflow),而后你修改100次,而后再把它显示出来

  • 不要把 DOM 结点的属性值放在一个循环里当成循环里的变量

    for(let i = 0; i < 1000; i++) {
        // 获取 offsetTop 会致使回流,由于须要去获取正确的值
        console.log(document.querySelector('.test').style.offsetTop)
    }
    复制代码
  • 不要使用 table 布局,可能很小的一个小改动会形成整个 table 的从新布局

  • 动画实现的速度的选择,动画速度越快,回流次数越多,也能够选择使用 requestAnimationFrame

  • CSS 选择符从右往左匹配查找,避免 DOM 深度过深

  • 将频繁运行的动画变为图层,图层可以阻止该节点回流影响别的元素。好比对于 video 标签,浏览器会自动将该节点变为图层。


这不是我想梳理的内容

上面的渲染流程是我一年前就在我笔记中存在的内容,我还记得当时学习的方式是囫囵吞枣式的,上面☝简略版的渲染流程,我印象中是在GitHub上面某博看看的,当时直接Copy下来的,当时以为这个渲染原理这块有了别人梳理好的结论,本身多看看,会记住的,事实上,面试的时候,提起这部分的时候,深度明显不够,天然就被问倒了

下来梳理了一份详细的版本,坦白说,做为一个学者,天然是站在巨人的肩膀上,去总结梳理知识,我认为这是对我最有效的学习方式

让我带着你🚗重温通常渲染流程吧


详细版的渲染机制

较为专业的术语总结为如下阶段:

  1. 构建DOM树
  2. 样式计算
  3. 布局阶段
  4. 分层
  5. 绘制
  6. 分块
  7. 光栅化
  8. 合成

你能够想象一下,从0,1字节流到最后页面展示在你面前,这里面渲染机制确定很复杂,因此渲染模块把执行过程当中化为不少的子阶段,渲染引擎从网络进程拿到字节流数据后,通过这些子阶段的处理,最后输出像素,这个过程能够称为渲染流水线 ,咱们从一张图上来看👇

那接下来就从每一个阶段来梳理一下大体过程。

构建DOM树

这个过程主要工做就是讲HTML内容转换为浏览器DOM树结构

  • 字节→字符→令牌→节点→对象模型(DOM)

文档对象模型(DOM)

<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html>
复制代码

咱们先看看数据是怎么样转换的👇

大概过程:

  1. **转换:**浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。
  2. **令牌化:**浏览器将字符串转换成 W3C HTML5 标准规定的各类令牌,例如,“”、“”,以及其余尖括号内的字符串。每一个令牌都具备特殊含义和一组规则。
  3. **词法分析:**发出的令牌转换成定义其属性和规则的“对象”。
  4. **DOM构建:**最后,因为 HTML 标记定义不一样标记之间的关系(一些标记包含在其余标记内),建立的对象连接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系:HTML 对象是 body 对象的父项,bodyparagraph 对象的父项,依此类推。

咱们把上述这样子的过程就叫作是构建DOM树过程

样式计算

这个子阶段主要有三个步骤

  • 格式化样式表
  • 标准化样式表
  • 计算每一个DOM节点具体样式

格式化样式表

咱们拿到的也就是0,1字节流数据,浏览器没法直接去识别的,因此渲染引擎收到CSS文本数据后,会执行一个操做,转换为浏览器能够理解的结构-styleSheets

若是你很想了解这个格式化的过程,能够好好去研究下,不一样的浏览器可能在CSS格式化过程当中会有所不一样,在这里就不展开篇幅了。

经过浏览器的控制台document.styleSheets能够来查看这个最终结果。经过JavaScript能够完成查询和修改功能,或者说这个阶段为后面的样式操做提供基石。

标准化样式表

什么是标准化样式表呢?先看一段CSS文本👇

body { font-size: 2em }
p {color:blue;}
span  {display: none}
div {font-weight: bold}
div  p {color:green;}
div {color:red; }
复制代码

有些时候,咱们写CSS 样式的时候,会写font-size:2em;color:red;font-weight:bold,像这些数值并不容易被渲染引擎所理解,所以须要在计算样式以前将它们标准化,如em->px,red->rgba(255,0,0,0),bold->700等等。

上面的代码标准后属性值是什么样子呢👇

计算每一个DOM节点具体样式

经过格式化标准化,接下来就是计算每一个节点具体样式信息了。

计算规则:继承层叠

继承:每一个子节点会默认去继承父节点的样式,若是父节点中找不到,就会采用浏览器默认的样式,也叫UserAgent样式

层叠:样式层叠,是CSS一个基本特征,它定义如何合并来自多个源的属性值的算法。某种意义上,它处于核心地位,具体的层叠规则属于深刻 CSS 语言的范畴,这里就补展开篇幅说了。

不过值得注意的是,在计算完样式以后,全部的样式值会被挂在到window.getComputedStyle当中,也就是能够经过JS来获取计算后的样式,很是方便。

这个阶段,完成了DOM节点中每一个元素的具体样式,计算过程当中要遵循CSS的继承层叠两条规则,最终输出的内容是每一个节点DOM的样式,被保存在ComputedStyle中。

想了解每一个 DOM 元素最终的计算样式,能够打开 Chrome 的“开发者工具”,选择第一个“element”标签,好比我下面就选择了div标签,而后再选择“Computed”子标签,以下图所示:

另一种说法CSSOM

若是不是很理解的话,能够看这里👇

跟处理HTML同样,咱们须要更具CSS两个规则:继承层叠转换成某种浏览器能理解和处理的东西,处理过程相似处理HTML,如上图☝

CSS 字节转换成字符,接着转换成令牌和节点,最后连接到一个称为“CSS 对象模型”(CSSOM) 的树结构内👇

不少人确定看这个很熟悉,确实,不少博客都是基于CSSOM说法来说的,我要说的是:

和DOM不同,源码中并无CSSOM这个词,因此不少文章说的CSSOM应该就是styleSheets,固然了这个styleSheets咱们能够打印出来的

不少文章说法是渲染树也是16年前的说法,如今代码重构了,咱们能够把LayoutTree当作是渲染树,不过它们之间仍是有些区别的。

生成布局树

上述过程已经完成DOM树(DOM树)构建,以及样式计算(DOM样式),接下来就是要经过浏览器的布局系统肯定元素位置,也就是生成一颗布局树(Layout Tree),以前说法叫 渲染树

建立布局树

  1. 在DOM树上不可见的元素,head元素,meta元素等,以及使用display:none属性的元素,最后都不会出如今布局树上,因此浏览器布局系统须要额外去构建一棵只包含可见元素布局树。

  2. 咱们直接结合图来看看这个布局树构建过程:

    为了构建布局树,浏览器布局系统大致上完成了下面这些工做:

  • 遍历DOM树可见节点,并把这些节点加到布局树中
  • 对于不可见的节点,head,meta标签等都会被忽略。对于body.p.span 这个元素,它的属性包含display:none,因此这个元素没有被包含进布局树。

布局计算

接下来就是要计算布局树节点的坐标位置,布局的计算过程很是复杂,张开介绍的话,会显得文章过于臃肿,大多数状况下,咱们只须要知道它所作的工做是什么,想知道它是如何作的话,能够看看如下两篇文章👇

梳理前三个阶段

一图归纳上面三个阶段

分层

  • 生成图层树(Layer Tree)
  • 拥有层叠上下文属性的元素会被提高为单独一层
  • 须要裁剪(clip)的地方也会建立图层
  • 图层绘制

首先须要知道的就是,浏览器在构建完布局树后,还须要进行一系列操做,这样子可能考虑到一些复杂的场景,好比一些些复杂的 3D 变换、页面滚动,或者使用 z-indexing 作 z 轴排序等,还有好比是含有层叠上下文如何控制显示和隐藏等状况。

生成图层树

你最终看到的页面,就是由这些图层一块儿叠加构成的,它们按照必定的顺序叠加在一块儿,就造成了最终的页面。

浏览器的页面实际上被分红了不少图层,这些图层叠加后合成了最终的页面。

咱们来看看图层与布局树之间关系,以下图👇

一般状况下,并非布局树的每一个节点都包含一个图层,若是一个节点没有对应的层,那么这个节点就从属于父节点的图层。

那什么状况下,渲染引擎会为特定的节点建立新图层呢?

有两种状况须要分别讨论,一种是显式合成,一种是隐式合成

显式合成

1、 拥有层叠上下文的节点。

层叠上下文也基本上是有一些特定的CSS属性建立的,通常有如下状况:

  1. HTML根元素自己就具备层叠上下文。
  2. 普通元素设置position不为static而且设置了z-index属性,会产生层叠上下文。
  3. 元素的 opacity 值不是 1
  4. 元素的 transform 值不是 none
  5. 元素的 filter 值不是 none
  6. 元素的 isolation 值是isolate
  7. will-change指定的属性值为上面任意一个。(will-change的做用后面会详细介绍)

2、须要剪裁(clip)的地方。

好比一个标签很小,50*50像素,你在里面放了很是多的文字,那么超出的文字部分就须要被剪裁。固然若是出现了滚动条,那么滚动条也会被单独提高为一个图层,以下图

数字1箭头指向的地方,能够看看,可能效果不是很明显,你们能够本身打开这个Layers探索下。

元素有了层叠上下文的属性或者须要被剪裁,知足其中任意一点,就会被提高成为单独一层。

隐式合成

这是一种什么样的状况呢,通俗意义上来讲,就是z-index比较低的节点会提高为一个单独的途图层,那么层叠等级比它高的节点都会成为一个独立的图层。

浏览器渲染流程&Composite(渲染层合并)简单总结

缺点: 根据上面的文章来讲,在一个大型的项目中,一个z-index比较低的节点被提高为单独图层后,层叠在它上面的元素通通都会提高为单独的图层,咱们知道,上千个图层,会增大内存的压力,有时候会让页面崩溃。这就是层爆炸

绘制

完成了图层的构建,接下来要作的工做就是图层的绘制了。图层的绘制跟咱们平常的绘制同样,每次都会把一个复杂的图层拆分为很小的绘制指令,而后再按照这些指令的顺序组成一个绘制列表,相似于下图👇

从图中能够看出,绘制列表中的指令其实很是简单,就是让其执行一个简单的绘制操做,好比绘制粉色矩形或者黑色的线等。而绘制一个元素一般须要好几条绘制指令,由于每一个元素的背景、前景、边框都须要单独的指令去绘制。

你们能够在 Chrome 开发者工具中在设置栏中展开 more tools, 而后选择Layers面板,就能看到下面的绘制列表:

在该图中,**箭头2指向的区域 **就是 document 的绘制列表,**箭头3指向的拖动区域 **中的进度条能够重现列表的绘制过程。

固然了,绘制图层的操做在渲染进程中有着专门的线程,这个线程叫作合成线程。

分块

  • 接下来咱们就要开始绘制操做了,实际上在渲染进程中绘制操做是由专门的线程来完成的,这个线程叫合成线程

  • 绘制列表准备好了以后,渲染进程的主线程会给合成线程发送commit消息,把绘制列表提交给合成线程。接下来就是合成线程一展宏图的时候啦。

你想呀,有时候,你的图层很大,或者说你的页面须要使用滚动条,而后页面的内容太多,多的没法想象,这个时候须要滚动很久才能滚动到底部,可是经过视口,用户只能看到页面的很小一部分,因此在这种状况下,要绘制出全部图层内容的话,就会产生太大的开销,并且也没有必要。

  • 基于上面的缘由,合成线程会讲图层划分为图块(tile)
  • 这些块的大小通常不会特别大,一般是 256 * 256 或者 512 * 512 这个规格。这样能够大大加速页面的首屏展现。

首屏渲染加速能够这么理解:

由于后面图块(非视口内的图块)数据要进入 GPU 内存,考虑到浏览器内存上传到 GPU 内存的操做比较慢,即便是绘制一部分图块,也可能会耗费大量时间。针对这个问题,Chrome 采用了一个策略: 在首次合成图块时只采用一个低分辨率的图片,这样首屏展现的时候只是展现出低分辨率的图片,这个时候继续进行合成操做,当正常的图块内容绘制完毕后,会将当前低分辨率的图块内容替换。这也是 Chrome 底层优化首屏加载速度的一个手段。

光栅化

接着上面的步骤,有了图块以后,合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操做是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。

  • 图块是栅格化执行的最小单位
  • 渲染进程中专门维护了一个栅格化线程池,专门负责把图块转换为位图数据
  • 合成线程会选择视口附近的图块(tile),把它交给栅格化线程池生成位图
  • 生成位图的过程实际上都会使用 GPU 进行加速,生成的位图最后发送给合成线程

运行方式以下👇

一般,栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。

相信你还记得,GPU 操做是运行在 GPU 进程中,若是栅格化操做使用了 GPU,那么最终生成位图的操做是在 GPU 中完成的,这就涉及到了跨进程操做。具体形式你能够参考下图:

从图中能够看出,渲染进程把生成图块的指令发送给 GPU,而后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中。

合成和显示

栅格化操做完成后,合成线程会生成一个绘制命令,即"DrawQuad",并发送给浏览器进程。

浏览器进程中的viz组件接收到这个命令,根据这个命令,把页面内容绘制到内存,也就是生成了页面,而后把这部份内存发送给显卡,那你确定对显卡的原理很好奇。

看了某博主对显示器显示图像的原理解释:

不管是 PC 显示器仍是手机屏幕,都有一个固定的刷新频率,通常是 60 HZ,即 60 帧,也就是一秒更新 60 张图片,一张图片停留的时间约为 16.7 ms。而每次更新的图片都来自显卡的前缓冲区。而显卡接收到浏览器进程传来的页面后,会合成相应的图像,并将图像保存到后缓冲区,而后系统自动将前缓冲区后缓冲区对换位置,如此循环更新。

这个时候,心中就有点概念了,好比某个动画大量占用内存时,浏览器生成图像的时候会变慢,图像传送给显卡就会不及时,而显示器仍是以不变的频率刷新,所以会出现卡顿,也就是明显的掉帧现象。


用一张图来总结👇

回流-重绘-合成

咱们把上面整个的渲染流水线,用一张图片更直观的表示👇

更新视图三种方式

  • 回流
  • 重绘
  • 合成

回流

另一个叫法是重排,回流触发的条件就是:对 DOM 结构的修改引起 DOM 几何尺寸变化的时候,会发生回流过程。

具体一点,有如下的操做会触发回流:

  1. 一个 DOM 元素的几何属性变化,常见的几何属性有widthheightpaddingmarginlefttopborder 等等, 这个很好理解。
  2. 使 DOM 节点发生增减或者移动
  3. 读写 offset族、scroll族和client族属性的时候,浏览器为了获取这些值,须要进行回流操做。
  4. 调用 window.getComputedStyle 方法。

一些经常使用且会致使回流的属性和方法:

  • clientWidthclientHeightclientTopclientLeft
  • offsetWidthoffsetHeightoffsetTopoffsetLeft
  • scrollWidthscrollHeightscrollTopscrollLeft
  • scrollIntoView()scrollIntoViewIfNeeded()
  • getComputedStyle()
  • getBoundingClientRect()
  • scrollTo()

依照上面的渲染流水线,触发回流的时候,若是 DOM 结构发生改变,则从新渲染 DOM 树,而后将后面的流程(包括主线程以外的任务)所有走一遍。

重绘

当页面中元素样式的改变并不影响它在文档流中的位置时(例如:colorbackground-colorvisibility等),浏览器会将新样式赋予给元素并从新绘制它,这个过程称为重绘。

根据概念,咱们知道因为没有致使 DOM 几何属性的变化,所以元素的位置信息不须要更新,从而省去布局的过程,流程以下:

跳过了布局树建图层树,直接去绘制列表,而后在去分块,生成位图等一系列操做。

能够看到,重绘不必定致使回流,但回流必定发生了重绘。

合成

还有一种状况:就是更改了一个既不要布局也不要绘制的属性,那么渲染引擎会跳过布局和绘制,直接执行后续的合成操做,这个过程就叫合成

举个例子:好比使用CSS的transform来实现动画效果,避免了回流跟重绘,直接在非主线程中执行合成动画操做。显然这样子的效率更高,毕竟这个是在非主线程上合成的,没有占用主线程资源,另外也避开了布局和绘制两个子阶段,因此相对于重绘和重排,合成能大大提高绘制效率。

利用这一点好处:

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快
  • 当须要 repaint 时,只须要 repaint 自己,不会影响到其余的层
  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint

提高合成层的最好方式是使用 CSS 的 will-change 属性

GPU加速缘由

好比利用 CSS3 的transformopacityfilter这些属性就能够实现合成的效果,也就是你们常说的GPU加速

  • 在合成的状况下,直接跳过布局和绘制流程,进入非主线程处理部分,即直接交给合成线程处理。
  • 充分发挥GPU优点,合成线程生成位图的过程当中会调用线程池,并在其中使用GPU进行加速生成,而GPU 是擅长处理位图数据的。
  • 没有占用主线程的资源,即便主线程卡住了,效果依然流畅展现。

实践意义

  • 使用createDocumentFragment进行批量的 DOM 操做
  • 对于 resize、scroll 等进行防抖/节流处理。
  • 动画使用transform或者opacity实现
  • 将元素的will-change 设置为 opacity、transform、top、left、bottom、right 。这样子渲染引擎会为其单独实现一个图层,当这些变换发生时,仅仅只是利用合成线程去处理这些变换,而不牵扯到主线程,大大提升渲染效率。
  • 对于不支持will-change 属性的浏览器,使用一个3D transform属性来强制提高为合成 transform: translateZ(0);
  • rAF优化等等

参考

你不知道的 Web Workers (上)[7.8K 字 | 多图预警]

极客时间-渲染流程

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理

How Browsers Work: Behind the scenes of modern web browsers

史上最全!图解浏览器的工做原理

W3C HTML5 标准

浏览器渲染流程&Composite(渲染层合并)简单总结

浏览器层合成与页面渲染优化

浏览器的回流与重绘 (Reflow & Repaint)

无线性能优化:Composite

CSS GPU Animation: Doing It Right

本文使用 mdnice 排版