回流与重绘
概念
- 回流:重新计算元素几何属性(布局阶段);重绘:重新绘制元素外观(绘制阶段)。
- 回流(reflow)是回到渲染管线中的布局阶段,但是注意这并不代表会重走一遍渲染管线布局之后的整个流程,在按顺序走完渲染管线之后每一次回流,重绘(repaint),修改CSS等的操作是独立的,比如修改一个元素的颜色,这对cssom造成了修改,而cssom的建立是在布局之前的,但是由于并没有触发布局的变化所以并没有回流,但是回流必定会重绘,这是因为布局发生了改变必须重新渲染才行
- 布局改变大概率dom树也改变了,但是dom树的改变不是回流的一部分,只是他造成的影响而已,只有改动layout树这个操作是回流
- 当页面布局被改变的时候(即这个改变会影响其他元素)会触发回流(在布局步骤),修改layout,而如果只是对自身某些不会影响其他元素的属性改动则不会触发回流,而是触发重绘(在绘制阶段),比如改变了颜色,透明度等(在没有独立图层的情况下,opacity如果是独立的图层那也不会触发重绘)
- 触发回流:width/height/margin/padding/border/display/position/float,offsetWidth 等属性读取,窗口 resize,字体变化,添加/删除 DOM 元素,改变内容(如文字)等
- 仅触发重绘:color/background/border-color/visibility/outline/box-shadow 等外观属性,以及
opacity(但 opacity 可能单独走合成)。
- 注意读取高度触发回流而读取color不触发是因为color不涉及布局而高度的值依赖于布局,所以为了获得精确的值需要回到布局阶段取拿
优化手段:
- 分层: 可以优化但无法阻止,will-change标记将要修改建议浏览器分层,另外absolution,fixed这些脱离文档流的(元素自身仍可能因位置移动回流)也会一定程度影响分层,但注意所有这些都只是影响,浏览器分层是用的自己的策略
- 虚拟dom: 批量的修改用虚拟dom完成布局和样式调整后再统一加入(框架已经做完了)
- 批量dom修改:基础js中用const fragment=document.createDocumentFragment(),这是个虚拟文档容器,document.body.appendChild会往dom树中添加,但fragment.appendChild只会加在这个虚拟文档容器上,最后document.body.appendChild()把fragment加到对应位置(这里是body,位置自己选)
- 使用transform替代位置变化等防止回流,也不触发重绘(例如transform向上移动覆盖了其他元素,但是因为transform移动元素的时候该元素会被提升为独立的合成层,而被覆盖元素的位图什么的都没有消失,只是被transform的元素遮住了而已所以没有重绘)
- transform做动画直接在最后合成阶段就行,但如果用width什么的做动画那每一帧都会回流一次重新布局
- 优先使用只影响元素自身的改动确保只是重绘避免回流