刚入行前端的时候是否是常常看到有文章说尽可能不要用CSS通配符*,CSS选择器层叠最好不要超过三层,HTML少使用table,结构也要尽可能简单一些...这一切说的不无道理,过多的使用确实会形成浏览器渲染的性能下降,当你认识了reflow和repaint以后,你会发现这些还真不能用太多。前端
1、浏览器渲染过程
不一样的浏览器渲染过程实际上并不相同(由渲染引擎决定),可是依旧存在一致的部分,大体过程以下图:浏览器

- 解析HTML以构建DOM树:渲染引擎解析HTML文档,转换树中的HTML标签或JS生成的标签生成DOM节点
- 解析CSS以构建样式结构体:渲染引擎解析CSS(包括外部CSS文件、样式元素以及JS生成的样式)成样式结构体,根据CSS选择器计算出节点的样式
- 构建渲染树:从根节点递归调用,计算每个元素的大小、位置以及每一个节点所应该出如今屏幕上的精确坐标
- 绘制渲染树:渲染引擎遍历渲染树将其绘制出来
2、什么是 reflow & repaint
其实在上面浏览器渲染过程当中的第三步和第四步分别就是回流(reflow)和重绘(repaint)。当第一次打开一个页面时,至少会有一次回流和重绘。以后,若是渲染树发生了变更,那么可能会触发回流或重绘中的一个或两者。缓存
回流:若是渲染树的节点发生告终构性变化,例如宽高、位置、隐藏上有变化时,那么就会触发一次回流
重绘:若是渲染树的节点发生了非结构性变化,例如背景色、颜色、字体上有变化时,那么就会触发一次重绘布局
回流必将引发重绘,而重绘不必定会引发回流。回流的成本比重绘的成本要高得多,由于一个节点的回流颇有可能致使子结点,父节点回流。性能
3、触发 reflow & repaint
- 页面初始化渲染
- DOM元素的添加、修改、删除
- 移动DOM或着DOM发生了动画
- resize浏览器窗口、滚动页面
- 修改DOM元素的字体颜色
- 激活CSS伪类
- 某个样式的添加、修改、删除
- display: none会触发reflow,visibility: hidden只会触发repaint,由于没有发生位置变化
- 读取元素的某些属性(没想到吧...)
现代浏览器会对回流作优化,它会等到足够数量的变化发生,再作一次批处理回流。可是当获取某些属性时,浏览器为了得到正确的值也会提早触发回流,这样就使得浏览器的优化失效了,这些属性包括offsetLeft、offsetTop、offsetWidth、offsetHeight、 scrollTop/Left/Width/Height、clientTop/Left/Width/Height、调用了getComputedStyle()或者IE的currentStyle字体
4、减小 reflow & repaint
回流和重绘是不可避免的,咱们只能说将它们对性能的影响减到最小,既然咱们知道什么状况会触发它们,那就从这些方面入手:优化
-
让须要改变的元素进行“离线处理”,处理完后一块儿更新动画
- 使用DocumentFragment进行缓存操做,触发一次回流和重绘
- 使用display: none,触发两次回流和重绘(因为display: none的元素不在渲染树中,对隐藏的元素操做不会引起其余元素的回流,能够先隐藏它,操做完成后再显示。这样只在隐藏和显示时触发2次回流)
- 将须要屡次回流的元素的position属性设为absolute或fixed(设为float没有彻底脱离文档流,这个很微妙),这样元素就脱离了文档流,它的变化不会影响到其余元素的布局,不会致使一个完整回流
- 不要把DOM节点的属性值放在一个循环里做为循环的变量,这会致使大量地读写这个节点的属性
- 不要一条一条地修改样式,将屡次改变样式属性的操做合并成一次(通常人也不会这样作)
- 不要用table布局,table中某个元素一旦触发回流就会致使table里全部的其它元素回流。在适合用table的场合,能够设置table-layout为auto或fixed,这样可让table一行一行的渲染,这种作法也是为了限制回流的影响范围(通常咱们能够经过ul li的布局替代之)
- 避免使用CSS的JavaScript表达式(这种规则已过期)
总之,在之后的开发中咱们要尽可能避免大量、频繁的操做DOM元素,由于DOM操做的代价实在是太昂贵了(这也是Virtual DOM应运而生的缘由)。在书写HTML时要避免没必要要的层级,书写CSS时避免嵌套过深、规则过于复杂,尤为是后代选择器,匹配选择器也会耗费更多的CPU。spa