最近看了幕课网 web 前端性能优化的课程,其中说到了浏览器的回流(reflow) 及 重绘(repaint)。以为之后面试或许会被问到因此作一下笔记:javascript
课程从回流及重绘这两个点延伸出了一个知识点就是 "css 会影响 javascrip 执行时间致使 javascript 脚本变慢"。css
浏览器渲染一个网页的时候会启用两条线程:一条渲染javascript 脚本,另外一条渲染 ui 即css 样式的渲染。但这两条线程是互斥的。当javascript 线程运行的时候 ui 线程则会停止暂停,反之亦然。html
那这是为何呢?前端
缘由是,当ui 线程运行对页面进行渲染的时候 js 脚本不免会涉及到页面视图上的一些样式的改变,为了使这个改变动加准确 js 脚本只好等待ui 线程渲染完成的时候才去执行。java
因此当一个页面的元素样式改动频繁的时候ui 线程就会持续渲染,形成js 代码反应慢半拍,卡顿的状况。web
那回流和重绘到底是什么呢?为何他们的威力会那么大呢?面试
回流:当render tree 的一部分或所有的元素因改变了自身的宽高,布局,显示或隐藏,或者元素内部的文字结构发生变化 致使须要从新构建页面的时候,回流就产生了chrome
重绘:当一个元素自身的宽高,布局,及显示或隐藏没有改变,而只是改变了元素的外观风格的时候,就会产生重绘。例如你改变了元素的background-color....canvas
所以得出了一个结论:浏览器
回流一定触发重绘,而重绘不必定触发回流
触发回流的css 属性有这些:
而触发重绘的有这些:
如今咱们知道了那些属性会触发回流,那如何避免回流呢?
其实有两个方法:第一,不使用以上能触发图层回流的属性,第二,创建一个图层,让回流在这些图层里面进行,限制回流和重绘的范围,减小浏览器的运算工做量
先说说图层吧
css 中有一个重要的概念的就是 "划分图层"。
在页面加载完dom以后,浏览器会根据自身制定的一些特殊css 属性对页面进行划分为一个个图层。例如:一些有着 position: absolute 属性的元素他们会被浏览器概括到一个图层中,而另一些 position: fixed 的元素也会被浏览器概括到另一个图层中,还有一些例如:transform: translateZ(0).... 这些属性都是视浏览器厂商制定的
图层的优势就是:当一个元素在本身图层内发生变化的时候它的回流和重绘只会在图层内部发生,从而减小了浏览器对于重绘与回流的运算量。
那图层内部究竟干了什么呢?
首先,图层对图层里面的节点作回流和重布局运算,计算出他们的样式
而后,对它们生成图形和位置
紧跟着,将每一个节点绘制填充到图层位图中
再而后,图层做为纹理发送到GPU 上
最后,将全部图层合并,显示在浏览器的页面上
图层虽好,可是若是滥用图层的话就会让页面由于进行了太多合并运算致使页面卡顿。
因此说,图层的用途应该让给那些常常要进行位置结构,布局变换的元素去使用。例如轮播图
那咱们如建立一个图层呢?
就拿chrome 来讲,建立一个图层要有如下其中一个条件:
1:一个dom 元素拥有 3d 或 透视变换的css 属性(persepective, transform)
2:video 标签
3:拥有3d 上下文或加速 2d 上下文的 canvas 节点
4:混合插件 flash
5:本身作的opacity 动画 或 使用一个动画 webkit 变换的元素
6:拥有 translate3d 或 translateZ 这两会使 GPU 加速的属性
7:一个包含复合层子节点的元素。(有点绕,能够这样想:其实自己整个网页页面就是一个图层,html 标签下包含着若干的标签 head, body,..... 这样便知足了这个条件了)
8:元素有一个其 z-index 比它低的兄弟节点。因为 z-index 控制的是元素上下层的关系,因此当上下层关系变换的时候就须要一个图层去渲染,所以知足这个条件的元素也会被建立一个图层
可是在个人实验中发现第7 和 8是没有被变成图层的,不知道为嘛.......
那若是咱们使用属性又有那些可替换的属性能够优化回流与重绘呢,一共有一下9点:
1:用transform 代替 top,left ,margin-top, margin-left... 这些位移属性
2:用opacity 代替 visibility,可是要同时有translate3d 或 translateZ 这些能够建立的图层的属性存在才能够阻止回流
可是第二点通过个人实验,发现若是不加 transform: translateZ(0) 配合opacity的话仍是会产生回流的,而只用visibility 就只会产生重绘不会回流
而 opacity 加上 transform: translateZ/3d 这个属性以后便不会发生回流和重绘了
3:不要使用 js 代码对dom 元素设置多条样式,选择用一个 className 代替之。
4:若是确实须要用 js 对 dom 设置多条样式那么能够将这个dom 先隐藏,而后再对其设置
5:不要在循环内获取dom 的样式例如:offsetWidth, offsetHeight, clientWidth, clientHeight... 这些。浏览器有一个回流的缓冲机制,即多个回流会保存在一个栈里面,当这个栈满了浏览器便会一次性触发全部样式的更改且刷新这个栈。可是若是你屡次获取这些元素的实际样式,浏览器为了给你一个准确的答案便会不停刷新这个缓冲栈,致使页面回流增长。
因此为了不这个问题,应该用一个变量保存在循环体外。
6:不要使用table 布局,由于table 的每个行甚至每个单元格的样式更新都会致使整个table 从新布局
7:动画的速度按照业务按需决定
8:对于频繁变化的元素应该为其加一个 transform 属性,对于视频使用video 标签
9:必要时能够开启 GPU 加速,可是不能滥用