Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

repaint/reflow #16

Open
RachelRen opened this issue Aug 28, 2018 · 0 comments
Open

repaint/reflow #16

RachelRen opened this issue Aug 28, 2018 · 0 comments

Comments

@RachelRen
Copy link
Owner

好久没有看这个问题,对这个知识点都忘的差不多了,现在想重新整理一下。

页面解析的过程大致可以分为以下几个步骤:

  1. 解析HTML构建DOMTree: 解析html文档,把文档中的html标签或者js生成的标签到DOM节点
  2. 解析css,构建render tree:解析css成样式结构体,根据css选择器计算出节点的样式,创建另一个树
  3. 布局render tree: 从根节点递归调用,计算每一个元素的大小,位置,给出每个节点应该出现在屏幕上的精确位置的坐标
  4. 绘制render tree:把每个节点绘制出来

两者的区别

render tree 和 dom tree的区别是: render tree中的每个node是有自己的样式的,而且render tree不包 含隐藏的节点(display:none 和 head节点),因为这些节点不会呈现出来,所以不包含到render tree中。 visibility:hidden 隐藏的元素会包含到render tree中,因为他是占布局的,影响布局。

reflow和repaint分别出现在第三步和第四步。
对于DOM结构中的每个元素都有自己的盒子模型,这些都需要浏览器根据各种样式来计算并将元素放到它该出现的位置,这个叫reflow;
当各种盒子的位置,大小以及其他属性,如颜色,字体大小都确定下来以后,浏览器会把这些元素绘制一遍,所以页面上出现了内容,这个叫做repaint.

在过程中,怎么会出现这些情况呢:

  1. 当render tree 中的一部分或全部因为元素的尺寸,布局,隐藏等改变而需要重新构建,这个就是reflow。在reflow的时候,浏览器会使render tree中受到影响的部分失效,并重新构造这部分render tree。当完成reflow时,浏览器会重新绘制受影响的部分到屏幕中,该过程成为repaint。
  2. 当render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不影响布局的,比如background-color,这个就叫repaint
  3. reflow必定会引起repaint,但是repaint不一定会引起reflow

怎么引起reflow/repaint

reflow的成本比repaint高很多。DOM tree中的每个节点都会有reflow方法,一个节点的reflow很可能会导致子节点,甚至父节点以及同级节点的reflow。

能引起reflow的方式:

  1. 增删改dom节点的时候
  2. 移动DOM位置,或者搞动画
  3. 修改css样式
  4. resize 滚动的时候
  5. 修改默认字体
  6. 设置style属性
  7. 计算offsetWidth和offsetHeight
  8. 激活伪类
  9. dom内容改变

引起repaint的操作: 一个元素的外观改变,但是没有改变布局的情况

  1. visibility
  2. outline
  3. background-color

对自己的opacity做CSS动画 是引起了一个新的图层

单纯修改opacity的值会引起 reflow 和 repaint的

translate不会引发 reflow

怎样减少reflow的影响

  1. 不要一条一条修改DOM样式。可以一次性操作

  2. 让需要操作的元素离线处理,处理完后再更新

    • 使用DocumentFragment进行缓存操作,引发一次reflow和repaint
    • 使用display: none 只引发两次reflow repaint(由于display属性为none的元素不在渲染树中,对隐藏的元素操 作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排。)
    • 使用cloneNode(true or false) 和 replaceChild 技术,引发一次回流和重绘
  3. 不要把 DOM 节点的属性值放在一个循环里当成循环里的变量。不然这会导致大量地读写这个结点的属性

  4. 尽可能的修改层级比较低的 DOM节点。当然,改变层级比较底的 DOM节点有可能会造成大面积的 reflow,但是也可能影响范围很小.(因为改变 DOM 树中的一级会导致所有层级的改变,上至根部,下至被改变节点的子节点。这导致大量时间耗费在执行 reflow 上面 )(减少不必要的 DOM 层级(DOM depth)。改变 DOM 树中的一级会导致所有层级的改变,上至根部,下至被改变节点的子节点。这导致大量时间耗费在执行 reflow 上面。)

  5. 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素为动画的 HTML 元素,例如动画,那么修改他们的 CSS 是会大大减小 reflow 。因为,它们不影响其他元素的布局,所它他们只会导致重新绘制,而不是一个完整回流。这样消耗会更低。

  6. 不要用tables布局的一个原因就是tables中某个元素一旦触发reflow就会导致table里所有的其它元素reflow。在适合用table的场合,可以设置table-layout为auto或fixed,这样可以让table一行一行的渲染,这种做法也是为了限制reflow的影响范围。

  7. 避免使用CSS的JavaScript表达式,如果css里有expression,每次都会重新计算一遍。

  8. 不要经常去访问计算后的样式,如果可以,可以先将这些信息缓存下来 .

  9. 请求如下值offsetTop, offsetLeft, offsetWidth, offsetHeight,scrollTop/Left/Width/Height,clientTop/Left/Width/Height,浏览器会发生reflow,建议将他们合并到一起操作,可以减少回流的次数.

  10. 避免不必要的复杂的 CSS 选择器,尤其是后代选择器(descendant selectors),因为为了匹配选择器将耗费更多的 CPU。

  11. css里不要有表达式expression

  12. 权衡速度的平滑。比如实现一个动画,以1个像素为单位移动这样最平滑,但reflow就会过于频繁,CPU很快就会被完全占用。如果以3个像素为单位移动就会好很多。

  13. 实现元素的动画,对于经常要进行回流的组件,要抽离出来,它的position属性应当设为fixed或absolute

补充说明:

正常的渲染大概可以分为下面几个流程:

  1. Recalculate style
  2. Layout (reflow)
  3. update layout tree
  4. paint (repaint)
  5. composite Layers

参考文章:

reflow(回流)和repaint(重绘)及其优化

reflow 和 repaint

前端性能优化 —— reflow(回流)和repaint(重绘)

reflow和repaint引发的性能问题

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant