在绘制阶段,系统会遍历渲染树,并调用(浏览器内部)渲染器的 paint
方法,将渲染器的内容绘制成位图。
绘制工作是使用用户界面基础组件完成的。你所看见的一切都会触发 paint
。包括拖动滚动条,鼠标选择中文字等这些完全不改变样式,只改变显示结果的动作都会触发 paint
。
paint
的工作就是把文档中用户可见的那一部分展现给用户。paint
是把 layout
和样式计算的结果直接在浏览器视窗上绘制出来,它并不实现具体的元素计算,只是 layout
后面的那一步。
CSS2 规范 定义了绘制流程的顺序。绘制的顺序其实就是元素进入 堆栈样式上下文 的顺序。这些堆栈会从后往前绘制,因此这样的顺序会影响绘制。块呈现器的堆栈顺序如下:
background-color
)background-image
)border
)outline
)在样式发生变化时,浏览器会尽可能做出最小的响应。因此,元素的颜色改变后,只会对该元素进行重绘;元素的位置改变后,会对该元素及其子元素(可能还有同级元素)进行布局和重绘;添加 DOM 节点后,会对该节点进行布局和重绘。一些重大变化(例如增大 <html>
元素的字体)会导致缓存无效,使得整个渲染树都会进行重新布局和绘制。
渲染管道中最重要的事情是:每个步骤中,前一个操作的结果用于后一个操作创建新数据。例如,如果布局树中的某些内容发生改变,需要为文档的受影响部分重新生成 绘制 指令。
如果要为元素设置动画,则浏览器必须在每个帧之间运行这些操作。大多数显示器每秒刷新屏幕 60 次(60 fps),当屏幕每帧都在变化,人眼会觉得动画很流畅。但是,如果动画丢失了中间一些帧,页面看起来就会卡顿(Janky)。
即使渲染操作能跟上屏幕刷新,这些计算也会在主线程上运行,这意味着当你的应用程序运行 JavaScript 时动画可能会被阻塞。
你可以将 JavaScript 操作划分为小块,并使用 requestAnimationFrame()
在每个帧上运行。
有关此主题的更多信息,请参阅 Optimize JavaScript Execution。你也可以在 Web Worker 中运行 JavaScript 以避免阻塞主线程。
重排(Reflow,也叫回流)指的是当浏览器某个位置的布局发生了变化,浏览器就会重新从根部开始递归向下计算该节点及其子孙节点的布局,依次计算所有节点的几何尺寸和位置。
正如上文所述,当 DOM 的结构发生了改变,需要从生成 DOM 这一步开始,重新经过 样式计算、生成布局树、建立图层树、再到 生成绘制列表 以及之后的显示器显示整个渲染过程走一遍,开销是非常大的。
在重排过程中,可能会增加一些渲染器,如文本字符串。DOM 树里的每个节点(内部)都会有 reflow
方法,一个节点的重排很有可能导致子节点,甚至父节点以及同级节点的重排。
重排后,浏览器会重新绘制受影响的部分到屏幕可视区域,该过程称为重绘。另外,DOM 变化不一定都会影响几何属性,比如改变一个元素的背景色不影响宽高,这种情况下只会发生重绘,代价较小。
重排几乎是无法避免的。现在界面上流行的一些效果,比如树状目录的折叠、展开(实质上是元素的显示与隐藏)等,都将引起浏览器的重排。鼠标滑过、点击等用户交互事件,只要这些行为引起了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引起它内部、周围甚至整个页面的重新渲 染。通常我们都无法预估浏览器到底会重排哪一部分的代码,它们都彼此相互影响着。
引发重排的根本原因:
会导致产生重排的场景:
offsetWidth
和 offsetHeight
等(Resize):hover
(StyleChange)浏览器并不会在我们进行上述操作时立即进行重排,浏览器会积攥一批 reflow
后批量进行重排。
不过有的操作会让浏览器立马进行重排,比如 窗口缩放,改变了页面默认的字体,或者说 获取以下这些值。
offsetTop
、offsetLeft
、offsetWidth
、offsetHeight
scrollTop
、scrollLeft
、scrollWidth
、scrollHeight
clientTop
、clientLeft
、clientWidth
、clientHeight
getComputedStyle()
和 currentStyle
触发页面重新布局的样式属性:
width top text-alignheight bottom vertical-alignpadding left line-heightmargin right overflowdisplay position font-familyborder float font-sizeborder-width clear white-spacemin-heightmin-width
减少重排的方案:
table
布局的另一个原因就是 tables
中某个元素一旦触发重排就会导致 table
里所有的其它元素重排。在适合用 table
的场合,可以设置 table-layout
为 auto
或 fixed
display
到页面上class
,然后修改 DOM 的 className
position
属性应当设为 fixed
或 absolute
重绘(Repaint)遍历所有节点,检测节点的可见性、颜色、轮廓等可见的样式属性,然后根据检测的结果更新页面的响应部分。当渲染树中的一些元素需要更新一些不会改变元素布局的属性,比如只是影响元素的外观、风格、而不会影响布局的那些属性,这时候就只发生重绘。当然,页面首次加载也是要重绘一次的。
光栅: 光栅主要是针对图形的一个栅格化过程。现代浏览器中主要的绘制工作主要用光栅化软件来完成。所以元素重绘由这个元素和绘制层级的关系,来决定的是否会很大程度影响你的性能,如果这个元素盖住的多层元素都被重新绘制,性能损耗当然大。
只触发重绘的属性
colorborder-styleborder-radiusvisibilitytext-decorationbackgroundbackground-imagebackground-positionbackground-repeatbackground-sizeoutline-coloroutlineoutline-styleoutline-widthbox-shadow
在网页元素发生变化时,浏览器会尽可能做出最小的响应:
<html>
元素的字体)会导致缓存无效,使得整个渲染树都会进行重新布局和绘制。