前言
这是一篇翻译文章, 源于我几周前在mdn
搜某一个属性的时候看到的, 具体哪一个属性我忘了, 但这篇文章我收藏了, 个人觉得写得挺好的, 于是想要分享给大家, 英文文章看起来相对有些吃力, 翻译成中文之后相对更容易看, 同时也可以练一练自己的英文能力, 当然也可以方便自己之后备查, 于是就有了这篇文章, 话不多说, 接下来就是这篇文章的正文了
为了便于将一些陌生的单词和原文进行对照, 有陌生单词的段落/语句也以引用的形式标注出来了, 文末还整理了一些陌生的单词
原文链接:Render-tree Construction, Layout, and Paint, 以及关于回流和重绘, 这篇文章写得挺不错的, 推荐大家看一看: 浏览器的回流与重绘 (Reflow & Repaint)
一棵渲染树
它包含CSSOM
树和DOM
树, 然后它会计算每个可见元素的布局, 并且充当一个绘制进程的输入来将像素渲染到屏幕上. 优化这里面的每一个步骤对于实现最佳渲染效果而言至关重要
The CSSOM and DOM trees are combined into a render tree, which is then used to compute the layout of each visible element and serves as an input to the paint process that renders the pixels to screen. Optimizing each of these steps is critical to achieving optimal rendering performance.
在上一节关于构建对象模型
的内容中, 我们根据HTML
和CSS
的输入建立了DOM
树和CSSOM
树. 然而, 他们都只是捕捉到了html
文件(档)不同方面的独立对象: 一个描述内容, 另一个描述需要应用到文档中的样式规则. 那么我们该如何合并这两者并且让浏览器将像素渲染到屏幕上呢?
太长不看
渲染树
由DOM
树和CSSOM
树组成渲染树
仅包含渲染页面所需的节点布局
计算每个对象的确切位置和大小- 最后一步是绘制, 它接收最终的
渲染树
并将像素渲染到屏幕上
第一个阶段: 渲染树阶段
首先, 浏览器将DOM
树和CSSOM
树组成渲染树
, 它捕捉了页面上所有可见的DOM
内容和每个节点的CSSOM
样式信息
为了构建渲染树
, 浏览器大致做了以下工作:
To construct the render tree, the browser roughly does the following:
- 从
DOM
树的根节点开始, 遍历每一个可见的节点
Starting at the root of the DOM tree, traverse each visible node.
- 有些节点是不可见的(比如`script`标签, `meta`标签等), 由于它们没有反映在渲染的输出中, 所以被省略了
- 有些节点被`CSS`样式规则所隐藏, 这样的节点也会从`渲染树`中省略; 比如上图中的`span`节点就没有出现在`渲染树`中, 因为我们有一个明确的规则, 这个规则给它设置了一个`display: none;`属性
2. 对于每一个可见的节点, 找到合适的匹配的CSSOM
规则并应用它们
- 发出带有内容及其计算样式的可见节点
作一个简短的补充: 请注意visibility: hidden;
和display: none;
是不一样的. 两者都能使元素不可见, 只是前者使元素依旧在布局中占据空间(也就是说, 有这个样式的元素会被渲染成一个空盒子); 而后者则是将元素完全从渲染树
中移除, 由此一来, 有这个样式的元素就不可见了, 同时也不是布局中的一部分, 二者使元素不可见的机制是不同的
As a brief aside, note that visibility: hidden is different from display: none. The former makes the element invisible, but the element still occupies space in the layout (that is, it's rendered as an empty box), whereas the latter (display: none) removes the element entirely from the render tree such that the element is invisible and is not part of the layout.
最终的输出是一个包含了屏幕上所有可见内容的内容和样式信息的渲染. 有了渲染树, 我们就可以进入'布局'阶段了
The final output is a render that contains both the content and style information of all the visible content on the screen. With the render tree in place, we can proceed to the "layout" stage.
原文为:
The final output is a render that contains both the content and style information of all the visible content on the screen. With the render tree in place, we can proceed to the "layout" stage.
第二个阶段: 布局阶段(回流)
到目前为止, 我们计算了哪些节点应该是可见的, 以及它们的计算样式, 但我们还没有计算它们在设备视口中的确切位置和大小: 这就是布局阶段
, 又名回流
Up to this point we've calculated which nodes should be visible and their computed styles, but we have not calculated their exact position and size within the viewport of the device---that's the "layout" stage, also known as "reflow."
为了弄清每个对象在页面上的确切尺寸和位置, 浏览器从渲染树
的根节点开始, 遍历它. 让来我们一起来看一个简单的, 可实操的例子:
Let's consider a simple, hands on example:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Critial Path: Hello world!</title>
</head>
<body>
<div style="width: 50%">
<div style="width: 50%">Hello world!</div>
</div>
</body>
</html>
上述所示页面中的body
标签里包含两个嵌套的div
: 把第一个(父元素)div
节点的显示尺寸设置为视口
宽度的50%
, 把第二个div
, 也就是被第一个(父元素)div
包裹着的div
的宽度设置为它父元素的50%
, 也就是视口
宽度的25%
布局
过程的输出是一个盒模型
, 它准确地捕获了视口
中每一个元素的确切位置和尺寸: 所有的相对测量值都转换成了屏幕上的绝对像素
第三个阶段: 绘制阶段(光栅化)
最后, 现在我们已经知道哪些节点可见, 以及他们的计算样式和几何形状了, 那么我们就可以将这些信息传递到最终阶段了, 最终阶段会将渲染树
中的每一个节点转换成屏幕上的实际像素. 这个步骤通常被称作绘制
或者光栅化
Finally, now that we know which nodes are visible, and their computed styles and geometry, we can pass this information to the final stage, which converts each node in the render tree to actual pixels on the screen. This step is often referred to as "painting" or "rasterizing."
这会花一些时间, 因为浏览器需要做相当多的工作. 然而, Chrome DevTools
可以给上述所有三个阶段提供一些见解. 让我们一起来看看我们一开始的hello world
例子的布局
阶段:
This can take some time because the browser has to do quite a bit of work. However, Chrome DevTools can provide some insight into all three of the stages described above. Let's examine the layout stage for our original "hello world" example:
这个调试我自己试了试, 打开试一试的网站, 开启应该是在 开发者工具>Performance选项卡>左侧黑色圆形按钮(Record), 网站打开挺快的, 但开启调试的等待时间实在是太久了, 接近30分钟......途中浏览器还崩溃了一次, 于是作罢
布局
事件捕获了时间线中的渲染树
结构, 位置和尺寸计算- 当
布局
完成, 浏览器发出Paint Setup
和Paint
事件, 从而将渲染树
转换成屏幕上的像素
执行渲染树
构建, 布局
和绘制
所需要的时间根据文档的大小, 应用的样式和运行的设备而不同: 文档尺寸越大, 浏览器需要做的工作就越多; 样式越复杂, 绘制
所需要的时间也就越多(比如, 绘制
纯色的开销较小, 而阴影的计算和渲染开销就比较大了)
页面终于在视口
中出现了:
下面对浏览器的处理步骤做一个简单的回顾:
Here's a quick recap of the browser's steps
- 处理
HTML
标记并构建DOM
树 - 处理
CSS
标记并构建CSSOM
树 - 将
DOM
和CSSOM
组合成一个渲染树
- 在
渲染树
上运行布局
,计算每个节点的几何形状 - 把各个节点
绘制
到屏幕上
我们的demo
页面可能看起来比较简单, 但渲染它需要做很多的工作. 如果DOM
或者CSSOM
被修改了, 那么你将不得不重复这个过程, 以弄清哪些像素需要在屏幕上重新渲染
优化渲染的关键在于将上述顺序中的步骤1到5的总执行时间降到最低. 这样做可以尽可能快的将内容渲染到屏幕上, 并且还能减少初次渲染之后屏幕更新的间隔时间; 也就是说, 为用户的交互带来了更高的刷新体验
Optimizing the critical rendering path is the process of minimizing the total amount of time spent performing steps 1 through 5 in the above sequence.
点击展开
本文中涉及的生词:
单词 | 释义 |
---|---|
critical /ˈkrɪtɪk(ə)l/ | adj. 极其重要的, 关键的 |
optimal /ˈɑːptɪməl/ | adj. 最佳的, 最适的 |
construction /kənˈstrʌkʃn/ | n. 建造, 构建, 构造, 结构 |
roughly /ˈrʌfli/ | adv. 粗略地, 大约, 粗糙地 |
traverse /trəˈvɜːrs/ | v. 横穿, 穿过 |
n. 越过, 穿过 | |
adj. 横贯的 (这里在计算机术语中是遍历的意思) | |
as a brief aside | 作一个简短的补充 |
that is | 即; 就是说; 换言之 |
proceed /proʊˈsiːd/ | v. 开始行动, 开展, 继续做某事(~with), <正式>行进,前往, 文中指进入, 进入下一个阶段的意思 |
up to this point | 到目前为止 |
hands-on /ˌhændzˈɑːn/ | adj. 动手的, 实际操作的 |
refer /rɪˈfɜːr/ | v. 提到,谈及(refer to); 描述,涉及(refer to); 查阅,参考(refer to); |
referred to | 被称作 |
now that | 既然; 现在; 由于; |
quite /kwaɪt/ | adv. 相当, 很; 非常; 完全地, 彻底地 |
insight /ˈɪnsaɪt/ | n. 洞悉, 了解; 洞察力 |
insight into | 深刻理解, 洞察...... |
examine /ɪɡˈzæmɪn/ | v. 检查, 调查; 考核, 测验 |
recap /ˈriːkæp/ | v. 扼要重述, 摘要说明; 翻新胎面 |
n. 扼要的重述, 概述; 翻新的轮胎 | |
minimizing /ˈmɪnɪmaɪzɪŋ/ | n. [数][自]极小化; 求最小参数值 |
adj. 极小值的 | |
v. 将……减到最少(minimize的ing形式) |