From 8a35a5905cf0a5e906fac241be04a3bdb602f168 Mon Sep 17 00:00:00 2001 From: Jason Ren <40999116+jasonren0403@users.noreply.github.com> Date: Mon, 30 Oct 2023 18:29:14 +0800 Subject: [PATCH] [zh-cn] Update 'how browsers work' --- .../performance/how_browsers_work/index.md | 82 ++++++++++--------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/files/zh-cn/web/performance/how_browsers_work/index.md b/files/zh-cn/web/performance/how_browsers_work/index.md index d7d0612b1c3fe0..b47fa5f71eec43 100644 --- a/files/zh-cn/web/performance/how_browsers_work/index.md +++ b/files/zh-cn/web/performance/how_browsers_work/index.md @@ -1,8 +1,12 @@ --- title: 渲染页面:浏览器的工作原理 slug: Web/Performance/How_browsers_work +l10n: + sourceCommit: f32c38c1245815c7f520730f0cdef54960e5cee1 --- +{{QuickLinksWithSubPages("Web/Performance")}} + 页面内容快速加载和流畅的交互是用户希望得到的 Web 体验,因此,开发者应力争实现这两个目标。 了解如何提升性能和感知性能,有助于了解浏览器的工作原理。 @@ -29,7 +33,7 @@ Web 性能优化的目标之一就是缩短导航完成所花费的时间,在 浏览器向**名称服务器**发起 DNS 查询请求,最终得到一个 IP 地址。第一次请求之后,这个 IP 地址可能会被缓存一段时间,这样可以通过从缓存里面检索 IP 地址而不是再通过名称服务器进行查询来加速后续的请求。 -通过主机名加载一个页面通常仅需要一次 DNS 查询。但是,对于页面指向的不同的主机名,则需要多次 DNS 查询。如果字体(fonts)、图像(images)、脚本(scripts)、广告(ads)和网站统计(metrics)都有不同的主机名,则需要对每一个主机名进行 DNS 查询。 +通过主机名加载一个页面通常仅需要一次 DNS 查询。但是,对于页面指向的不同的主机名,则需要多次 DNS 查询。如果字体(font)、图像(image)、脚本(script)、广告(ads)和网站统计(metric)都有不同的主机名,则需要对每一个主机名进行 DNS 查询。 ![移动终端的请求先发送到基站,接着发送到运营商的中心计算机,然后再发送到互联网](latency.jpg) @@ -43,7 +47,7 @@ TCP 的“三次握手”技术经常被称为“SYN-SYN-ACK”——更确切 ### TLS 协商 -为了在 HTTPS 上建立安全连接,另一种握手是必须的。更确切的说是 {{glossary('TLS')}} 协商,它决定了什么密码将会被用来加密通信,验证服务器,在进行真实的数据传输之前建立安全连接。在发送真正的请求内容之前还需要三次往返服务器。 +对于通过 HTTPS 建立的安全连接,还需要另一次 "握手"。这种握手,或者说 {{glossary('TLS')}} 协商,决定使用哪种密码对通信进行加密,验证服务器,并在开始实际数据传输前建立安全连接。这就需要在实际发送内容请求之前,再往返服务器五次。 ![DNS 查询、TCP 握手和 TLS 5 步握手(包括服务器和客户端之间的 clienthello、serverhello 以及证书、clientkey 和完成消息)。](ssl.jpg) @@ -57,41 +61,41 @@ TCP 的“三次握手”技术经常被称为“SYN-SYN-ACK”——更确切 ```html - + - My simple page - + 简单的页面 + -

My Page

-

A paragraph with a link

+

我的页面

+

含有链接的段落。

- image description + 图像描述
``` -初始请求的响应包含所接收数据的第一个字节。{{glossary('Time to First Byte')}}(TTFB)是用户通过点击链接进行请求与收到第一个 HTML 数据包之间的时间。第一个内容分块通常是 14KB 的数据。 +初始请求的响应包含所接收数据的第一个字节。{{glossary('Time to First Byte','首字节时间(TTFB)')}}是用户通过点击链接进行请求与收到第一个 HTML 数据包之间的时间。第一个内容分块通常是 14KB 的数据。 -上面的例子中,这个请求肯定是小于 14KB 的,但是直到浏览器在解析阶段遇到链接时才会去请求链接的资源,下面有进行描述。 +上面的示例中,这个请求肯定是小于 14KB 的,但是直到浏览器在解析阶段遇到链接时才会去请求链接的资源,下面有进行描述。 -### TCP 慢启动 / 14KB 规则 +### 拥塞控制 / TCP 慢启动 -第一个响应数据包是 14KB 大小的。这是慢启动的一部分,慢启动是一种均衡网络连接速度的算法。慢启动逐渐增加发送数据的数量直到达到网络的最大带宽。 +TCP 数据包在传输过程中被分成若干段。由于 TCP 保证数据包的顺序,因此服务器在发送一定数量的数据包后,必须以 ACK 数据包的形式收到客户端的确认。 -在 {{glossary('TCP slow start', "TCP 慢启动")}} 中,在收到初始包之后,服务器会将下一个数据包的大小加倍到大约 28KB。后续的数据包依次是前一个包大小的二倍直到达到预定的阈值,或者遇到拥塞。 +如果服务器在每个网段后都等待 ACK,则会导致客户端频繁发出 ACK,即使在网络负荷较低的情况下也会增加传输时间。 -![TCP 慢启动](congestioncontrol.jpg) +另一方面,一次性发送过多网段可能会导致这样的问题:在繁忙的网络中,客户端无法接收到网段,只能长时间不停地回应 ACK,服务器不得不不断重新发送网段。 -如果你听说过初始页面加载的 14KB 规则,TCP 慢启动就是初始响应为 14KB 的原因,也是为什么 web 性能优化需要将此初始 14KB 响应作为优化重点的原因。TCP 慢启动逐渐建立适合网络能力的传输速度,以避免拥塞。 +为了平衡传输段的数量,{{glossary('TCP slow start', 'TCP 慢启动')}} 算法用于逐渐增加传输数据量,直到确定最大网络带宽,并在网络负载较高时减少传输数据量。 -### 拥塞控制 +传输段的数量由拥塞窗口(CWND)的值控制,该值可初始化为 1、2、4 或 10 MSS(以太网协议中的 MSS 为 1500 字节)。该值是发送的字节数,客户端收到后必须发送 ACK。 -当服务器用 TCP 数据包来发送数据时,客户端通过返回确认帧来确认传输。由于硬件和网络条件,连接的容量是有限的。如果服务器太快地发送太多的包,它们可能会被丢弃。这意味着,将不会有确认帧的返回。服务器把它们当做确认帧丢失。拥塞控制算法使用这个发送包和确认帧流来确定发送速率。 +如果收到 ACK,那么 CWND 值将加倍,这样服务器下次就能发送更多的数据段。相反,如果没有收到 ACK,那么 CWND 值将减半。因此,这种机制在发送过多网段和过少网段之间取得了平衡。 ## 解析 @@ -103,9 +107,9 @@ DOM 是浏览器标记的内部表示。DOM 也是被暴露的,可以通过 Ja ### 构建 DOM 树 -我们描述五个步骤在[关键渲染路径](/zh-CN/docs/Web/Performance/Critical_rendering_path)这篇文章中。 +我们在[关键渲染路径](/zh-CN/docs/Web/Performance/Critical_rendering_path)这篇文章中描述了五个步骤。 -第一步是处理 HTML 标记并构造 DOM 树。HTML 解析涉及到 [tokenization](/zh-CN/docs/Web/API/DOMTokenList) 和树的构造。HTML 标记包括开始和结束标记,以及属性名和值。如果文档格式良好,则解析它会简单而快速。解析器将标记化的输入解析到文档中,构建文档树。 +第一步是处理 HTML 标记并构造 DOM 树。HTML 解析涉及到[符号化](/zh-CN/docs/Web/API/DOMTokenList)和树的构造。HTML 标记包括开始和结束标记,以及属性名和值。如果文档格式良好,则解析它会简单而快速。解析器将标记化的输入解析到文档中,构建文档树。 DOM 树描述了文档的内容。[``](/zh-CN/docs/Web/HTML/Element/html) 元素是第一个标签也是文档树的根节点。树反映了不同标记之间的关系和层次结构。嵌套在其他标记中的标记是子节点。DOM 节点的数量越多,构建 DOM 树所需的时间就越长。 @@ -118,9 +122,9 @@ DOM 树描述了文档的内容。[``](/zh-CN/docs/Web/HTML/Element/html) 浏览器构建 DOM 树时,这个过程占用了主线程。当这种情况发生时,预加载扫描仪将解析可用的内容并请求高优先级资源,如 CSS、JavaScript 和 web 字体。多亏了预加载扫描器,我们不必等到解析器找到对外部资源的引用来请求它。它将在后台检索资源,以便在主 HTML 解析器到达请求的资源时,它们可能已经在运行,或者已经被下载。预加载扫描仪提供的优化减少了阻塞。 ```html - + -image description +图像描述 ``` @@ -136,45 +140,45 @@ DOM 树描述了文档的内容。[``](/zh-CN/docs/Web/HTML/Element/html) CSSOM 树包括来自用户代理样式表的样式。浏览器从适用于节点的最通用规则开始,并通过应用更具体的规则递归地优化计算的样式。换句话说,它级联属性值。 -构建 CSSOM 非常非常快,并且在当前的开发工具中没有以独特的颜色显示。相反,开发人员工具中的“重新计算样式”显示解析 CSS、构建 CSSOM 树和递归计算计算样式所需的总时间。在 web 性能优化方面,它是可轻易实现的,因为创建 CSSOM 的总时间通常小于一次 DNS 查询所需的时间。 +构建 CSSOM 非常快,并且在当前的开发工具中没有以独特的颜色显示。相反,开发人员工具中的“重新计算样式”显示解析 CSS、构建 CSSOM 树和递归计算计算样式所需的总时间。在 web 性能优化方面,它是可轻易实现的,因为创建 CSSOM 的总时间通常小于一次 DNS 查询所需的时间。 ### 其他过程 #### JavaScript 编译 -当 CSS 被解析并创建 CSSOM 时,其他资源,包括 JavaScript 文件正在下载(借助预加载扫描器)。JavaScript 被解释、编译、解析和执行。脚本被解析为抽象语法树。一些浏览器引擎使用[抽象语法树](https://zh.wikipedia.org/wiki/抽象语法树)并将其传递到解释器中,输出在主线程上执行的字节码。这就是所谓的 JavaScript 编译。 +在解析 CSS 和创建 CSSOM 的同时,包括 JavaScript 文件在内的其他资源也在下载(这要归功于预加载扫描器)。JavaScript 会被解析、编译和解释。脚本被解析为抽象语法树。有些浏览器引擎会将[抽象语法树](https://zh.wikipedia.org/wiki/抽象语法树)输入编译器,输出字节码。这就是所谓的 JavaScript 编译。大部分代码都是在主线程上解释的,但也有例外,例如在 [web worker](/zh-CN/docs/Web/API/Web_Workers_API) 中运行的代码。 -#### 构建辅助功能树 +#### 构建无障碍树 -浏览器还构建辅助设备用于分析和解释内容的辅助功能([accessibility](/zh-CN/docs/Learn/Accessibility))树。无障碍对象模型(AOM)类似于 DOM 的语义版本。当 DOM 更新时,浏览器会更新辅助功能树。辅助技术本身无法修改无障碍树。 +浏览器还构建辅助设备用于分析和解释内容的[无障碍](/zh-CN/docs/Learn/Accessibility)树。无障碍对象模型(AOM)类似于 DOM 的语义版本。当 DOM 更新时,浏览器会更新辅助功能树。辅助技术本身无法修改无障碍树。 -在构建 AOM 之前,屏幕阅读器([screen readers](/zh-CN/docs/Web/Accessibility/ARIA/ARIA_Screen_Reader_Implementors_Guide))无法访问内容。 +在构建 AOM 之前,[屏幕阅读器](/zh-CN/docs/Web/Accessibility/ARIA/ARIA_Screen_Reader_Implementors_Guide)无法访问内容。 ## 渲染 -渲染步骤包括样式、布局、绘制,在某些情况下还包括合成。在解析步骤中创建的 CSSOM 树和 DOM 树组合成一个 Render 树,然后用于计算每个可见元素的布局,然后将其绘制到屏幕上。在某些情况下,可以将内容提升到它们自己的层并进行合成,通过在 GPU 而不是 CPU 上绘制屏幕的一部分来提高性能,从而释放主线程。 +渲染步骤包括样式、布局、绘制,在某些情况下还包括合成。在解析步骤中创建的 CSSOM 树和 DOM 树组合成一个渲染树,然后用于计算每个可见元素的布局,然后将其绘制到屏幕上。在某些情况下,可以将内容提升到它们自己的层并进行合成,通过在 GPU 而不是 CPU 上绘制屏幕的一部分来提高性能,从而释放主线程。 -### Style +### 样式 -第三步是将 DOM 和 CSSOM 组合成一个 Render 树,计算样式树或渲染树从 DOM 树的根开始构建,遍历每个可见节点。 +关键呈现路径的第三步是将 DOM 和 CSSOM 组合成渲染树。计算样式树或渲染树的构建从 DOM 树的根开始,遍历每个可见节点。 -像 [``](/zh-CN/docs/Web/HTML/Element/head) 和它的子节点以及任何具有 `display: none` 样式的结点,例如 `script { display: none; }`(在 user agent stylesheets 可以看到这个样式)这些标签将不会显示,也就是它们不会出现在 Render 树上。具有 `visibility: hidden` 的节点会出现在 Render 树上,因为它们会占用空间。由于我们没有给出任何指令来覆盖用户代理的默认值,因此上面代码示例中的 `script` 节点将不会包含在 Render 树中。 +不会被显示的元素,如 [``](/zh-CN/docs/Web/HTML/Element/head) 元素及其子元素,以及任何带有 `display: none` 的节点,如用户代理样式表中的 `script { display: none; }`,都不会包含在渲染树中,因为它们不会出现在渲染输出中。应用了 `visibility: hidden` 的节点会包含在渲染树中,因为它们会占用空间。由于我们没有给出任何指令来覆盖用户代理默认值,因此上述代码示例中的 `script` 节点不会包含在渲染树中。 -每个可见节点都应用了其 CSSOM 规则。Render 树保存所有具有内容和计算样式的可见节点——将所有相关样式匹配到 DOM 树中的每个可见节点,并根据 [CSS 级联](/zh-CN/docs/Web/CSS/Cascade)确定每个节点的计算样式。 +每个可见节点都应用了 CSSOM 规则。渲染树包含所有可见节点的内容和计算样式,将所有相关样式与 DOM 树中的每个可见节点匹配起来,并根据 [CSS 级联](/zh-US/docs/Web/CSS/Cascade),确定每个节点的计算样式。 -### Layout +### 布局 -第四步是在渲染树上运行布局以计算每个节点的几何体。布局是确定呈现树中所有节点的宽度、高度和位置,以及确定页面上每个对象的大小和位置的过程。回流是对页面的任何部分或整个文档的任何后续大小和位置的确定。 +第四步是在渲染树上运行布局以计算每个节点的几何体。*布局*是确定呈现树中所有节点的宽度、高度和位置,以及确定页面上每个对象的大小和位置的过程。回流是对页面的任何部分或整个文档的任何后续大小和位置的确定。 构建渲染树后,开始布局。渲染树标识显示哪些节点(即使不可见)及其计算样式,但不标识每个节点的尺寸或位置。为了确定每个对象的确切大小和位置,浏览器从渲染树的根开始遍历它。 -在网页上,大多数东西都是一个盒子。不同的设备和不同的桌面意味着无限数量的不同的视区大小。在此阶段,考虑到视区大小,浏览器将确定屏幕上所有不同框的尺寸。以视区的大小为基础,布局通常从 body 开始,用每个元素的框模型属性排列所有 body 的子孙元素的尺寸,为不知道其尺寸的替换元素(例如图像)提供占位符空间。 +在网页上,大多数东西都是一个盒子。不同的设备和不同的桌面意味着无限数量的不同的视区大小。在此阶段,考虑到视口大小,浏览器将确定屏幕上所有不同框的尺寸。以视口的大小为基础,布局通常从 body 开始,用每个元素的框模型属性排列所有 body 的子孙元素的尺寸,为不知道其尺寸的替换元素(例如图像)提供占位符空间。 -第一次确定节点的大小和位置称为布局。随后对节点大小和位置的重新计算称为回流。在我们的示例中,假设初始布局发生在返回图像之前。由于我们没有声明图像的大小,因此一旦知道图像大小,就会有回流。 +第一次确定节点的大小和位置称为布局。随后对节点大小和位置的重新计算称为回流。在我们的示例中,假设初始布局发生在返回图像之前。由于我们没有声明图像的尺寸,因此一旦知道图像的尺寸,就会出现回流。 ### 绘制 -最后一步是将各个节点绘制到屏幕上,第一次出现的节点称为 [first meaningful paint](/zh-CN/docs/Glossary/first_meaningful_paint)。在绘制或光栅化阶段,浏览器将在布局阶段计算的每个框转换为屏幕上的实际像素。绘画包括将元素的每个可视部分绘制到屏幕上,包括文本、颜色、边框、阴影和替换的元素(如按钮和图像)。浏览器需要非常快地完成这项工作。 +最后一步是将各个节点绘制到屏幕上,第一次出现的节点称为 [first meaningful paint](/zh-CN/docs/Glossary/First_meaningful_paint)。在绘制或光栅化阶段,浏览器将在布局阶段计算的每个框转换为屏幕上的实际像素。绘画包括将元素的每个可视部分绘制到屏幕上,包括文本、颜色、边框、阴影和替换的元素(如按钮和图像)。浏览器需要非常快地完成这项工作。 为了确保平滑滚动和动画,占据主线程的所有内容,包括计算样式,以及回流和绘制,必须让浏览器在 16.67 毫秒内完成。在 2048x1536 分辨率的 iPad 上,有超过 314.5 万像素将被绘制到屏幕上。那是很多像素需要快速绘制。为了确保重绘的速度比初始绘制的速度更快,屏幕上的绘图通常被分解成数层。如果发生这种情况,则需要进行合成。 @@ -182,7 +186,7 @@ CSSOM 树包括来自用户代理样式表的样式。浏览器从适用于节 分层确实可以提高性能,但是它以内存管理为代价,因此不应作为 web 性能优化策略的一部分过度使用。 -### Compositing +### 合成 当文档的各个部分以不同的层绘制,相互重叠时,必须进行合成,以确保它们以正确的顺序绘制到屏幕上,并正确显示内容。 @@ -190,9 +194,9 @@ CSSOM 树包括来自用户代理样式表的样式。浏览器从适用于节 ## 交互 -一旦主线程绘制页面完成,你会认为我们已经“准备好了”,但事实并非如此。如果加载包含 JavaScript(并且延迟到 [`onload`](/zh-CN/docs/Web/API/GlobalEventHandlers/onload) 事件激发后执行),则主线程可能很忙,无法用于滚动、触摸和其他交互。 +一旦主线程绘制页面完成,你会认为我们已经“准备好了”,但事实并非如此。如果加载包含 JavaScript(并且延迟到 [`onload`](/zh-CN/docs/Web/API/GlobalEventHandlers/onload) 事件触发后执行),则主线程可能很忙,无法用于滚动、触摸和其他交互。 -{{glossary('Time to Interactive')}}(TTI)是测量从第一个请求导致 DNS 查询和 SSL 连接到页面可交互时所用的时间——可交互是 {{glossary('First Contentful Paint')}} 之后的时间点,页面在 50ms 内响应用户的交互。如果主线程正在解析、编译和执行 JavaScript,则它不可用,因此无法及时(小于 50ms)响应用户交互。 +{{glossary('Time to Interactive', '可交互时间(TTI)')}}是测量从第一个请求导致 DNS 查询和 SSL 连接到页面可交互时所用的时间——可交互是 {{glossary('First Contentful Paint')}} 之后的时间点,页面在 50ms 内响应用户的交互。如果主线程正在解析、编译和执行 JavaScript,则它不可用,因此无法及时(小于 50ms)响应用户交互。 在我们的示例中,可能图像加载很快,但 `anotherscript.js` 文件可能是 2MB,而且用户的网络连接很慢。在这种情况下,用户可以非常快地看到页面,但是在下载、解析和执行脚本之前,就无法滚动。这不是一个好的用户体验。避免占用主线程,如下面的网页测试示例所示: