如下代码
浏览器执行了handleResize、handleClick分别干了什么事情
1 | <style> |
浏览器渲染文档的步骤
- 处理HTML标记并构建DOM树
- 处理CSS标记并构建CSSOM树
- 合并DOM&CSSOM 为一个渲染树RenderTree, display: none 的元素并不会出现在RenderTree
- 根据RenderTree, 布局(计算节点在页面上的大小和位置)
- 将节点绘制到屏幕
- 渲染层合并(见下篇文章)
回到上述问题, resize时浏览器干了啥?
- 浏览器窗口resize时, 执行上述步骤 1 -> 4
- 确定哪些元素需要Reflow, 或者Repaint: ele1元素需要Reflow, ele2需要Repaint
- 浏览器执行Reflow -> Repaint, 这个过程浏览器会使用批处理优化
click的时候浏览器干了啥?
https://developers.google.com/web/fundamentals/design-and-ux/animations/animations-and-performance
- css translation动画, 会将transform || opacity 的元素提升一个复合层.
- will-change: transform || opacity; 会告诉浏览器在做更新前就使用最合适的优化, 进行高度优化. 也会将元素强行提升至一个复合层.
- 使用js做动画时, js必须在动画的每一帧计算元素的状态, 发送给GPU, 不会将元素提升至一个复合层, 想让元素提升至复合层, 必须使用translateZ或者will-change: transform, opacity;
1 | // 方式一: CSS动画: 设置以下CSS样式, hover时动画时, 查看Chrome Console 的Layers, 会有两个layer层; |
先了解下浏览器的回流和重绘
- 浏览器使用流式布局模型(Flow Based Layout)
- 由于浏览器使用流式布局, 对Render Tree的计算通常遍历一次即可, 遇到table或者内部元素通常要花3倍等同元素, 因此避免使用table布局
- 回流一定会导致重绘, 重绘未必会引起回流
回流 Reflow
Render Tree
中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程成为回流。
导致回流的操作:
- 页面首次渲染
- 浏览器窗口大小改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或者图片大小等等)
- 元素字体大小变化
- 添加或删除可见的DOM元素
- 激活CSS伪类(:hover)
- 查询某些属性或调用某些方法
导致回流的属性和方法
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- scrollIntoView()、scrollIntoViewIfNeeded()
- getComputedStyle()
- getBoundingClientRect()
- scrollTo()
重绘 Repaint
当页面中元素样式的改变并不影响它在文档流中的位置时 (例如: color
、background-color
、visibility
…), 浏览器会将新样式赋予给元素并重新绘制。
浏览器的优化会维护一个队列, 引起回流和重绘的操作放在队列中, 达到一定的阀值会清空队列, 进行一次批处理, 多次合成一次.
如果访问以下属性或方法, 浏览器会立刻清空队列, 确保你拿到的值是最精确的
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- width、height
- getComputedStyle()
- getBoundingClientRect()
避免回流或重绘
CSS
- 避免使用table布局
- 尽量在DOM树的最末端改变class
- 避免设置多层内联样式
- 将动画应用在position属性位absolute或fixed的元素
- 避免使用css表达式: calc()
JS
- 避免频繁操作样式, 一次性重写style, 通过把样式写在class中, 一次性更改class属性
- 避免频繁操作DOM, 创建一个documentFragment, 在其上应用所有DOM操作, 之后再把dF添加到文档中
- 先将元素设置display: none, 操作结束后再显示出来: display: none的元素进行DOM操作不会引发回流和重绘
- 避免频繁读取会引起回流/重绘的属性, 如需多次使用, 使用变量缓存起来
- 复杂动画的元素使用绝对定位, 使其脱离文档流. 避免引起父元素和后续元素的频繁回流