初入前端行业,是不是经常看到文章说尽量不要使用CSS通配符*,CSS选择器级联不要超过三层,HTML要少用表格,结构要尽可能简单……这一切都是有道理的。过度使用确实会降低浏览器渲染的性能。当你熟悉了reflow和repaint之后,你会发现这些真的不能用太多。1.浏览器渲染过程不同浏览器的渲染过程其实是不一样的(由渲染引擎决定),但还是有一致的部分。大致流程如下:解析HTML构建DOM树:渲染引擎??解析HTML文档,将树中的HTML标签或JS生成的标签转换生成DOM节点,解析CSS构建样式结构:渲染引擎??解析将CSS(包括外部CSS文件、样式元素、JS生成的样式)转化为样式结构,根据CSS选择器计算节点的样式构建渲染树:从根节点开始递归调用,计算大小和位置每个元素的位置,以及每个节点应该出现在屏幕上的精确坐标绘制渲染树:渲染引擎??遍历渲染树来绘制它2.什么是reflow&repaint。其实上面浏览器渲染过程中的第三步和第四步分别是reflow和repaint。第一次打开一个页面时,至少会有一次重排和重绘。稍后,如果渲染树发生变化,则可能会触发回流或重绘之一或两者。Reflow:如果渲染树的节点有结构变化,比如宽度、高度、位置、隐藏等变化,那么会触发reflow重绘:如果渲染树的节点有非结构变化,比如背景颜色,当颜色或者字体有变化的时候,就会触发重绘,回流肯定会引起重绘,但是重绘不一定会引起回流。回流的代价要比重绘的代价高很多,因为一个节点的回流很可能会引起子节点和父节点的回流。3.触发reflow&repaint页面初始化渲染DOM元素。添加、修改和删除移动的DOM或DOM动画。调整浏览器窗口大小并滚动页面。修改DOM元素的字体颜色。删除display:none会触发reflow,而visibility:hidden只会触发repaint,因为没有positionchange去读取元素的一些属性(没想到。。。)现代浏览器会优化reflow,等到够了发生变化,做另一批回流焊。但是,在获取某些属性时,浏览器也会为了获取正确的值而提前触发回流,这使得浏览器的优化失效。这些属性包括offsetLeft,offsetTop,offsetWidth,offsetHeight,scrollTop/Left/Width/Height,clientTop/Left/Width/Height,调用getComputedStyle()或者IE的currentStyle4.减少回流&重绘回流和重绘是不可避免的,我们只能说使它们对性能的影响最小化,既然我们知道什么情况会触发它们,那么就从这几个方面入手:让需要改变的元素“离线处理”,处理完后一起更新,使用DocumentFragment进行缓存操作,triggerareflowandredrawusingdisplay:none,triggertworeflowsandRedraw(由于display:none元素不在渲染树中,对隐藏元素的操作不会引起其他元素的回流,可以先隐藏,操作完成后再显示,这样隐藏和显示时只会触发2次回流)设置需要多次回流的元素的position属性为absolute或fixed(设置ittofloat并没有完全脱离文档流,这是很微妙的),这样元素就脱离了文档流,它的变化不会影响其他元素的布局。它不会导致完全回流。不要把DOM节点的属性值作为循环变量放在一个循环中,这样会导致大量读写这个节点的属性。不要一个一个修改style,把多次改变style属性的操作组合起来做一次(一般人也不会这样做)。不要使用表格布局。一旦表中的某个元素触发回流,就会导致表中所有其他元素回流。在适合表格的场合,可以将table-layout设置为auto或者fixed,这样表格就可以逐行渲染。这种做法也是为了限制reflow的影响范围(一般我们可以用ulli的布局来代替)避免CSS使用JavaScript表达式(这个规则已经过时了)总之,在以后的开发中,我们应该尽量避免对DOM元素进行大量频繁的操作,因为DOM操作的成本太昂贵了(这就是VirtualDOM应运而生的原因)。写HTML的时候要避免不必要的层,写CSS的时候要避免嵌套太深、规则太复杂,尤其是后代选择器,匹配选择器也会消耗更多的CPU。
