浏览器如何呈现网页
我的想法是:如果要构建快速可靠的网站,我需要真正理解浏览器渲染网页的每个步骤的机制,以便在开发过程中能够考虑到每个步骤并进行优化。这篇文章总结了我对端到端流程的学习,并且从较高的层次进行了阐述。
本文很大程度上基于Ilya Grigorik和Cameron Pittman在Udacity上开设的精彩(而且免费!)的网站性能优化课程。我强烈推荐大家去学习一下。
Paul Irish和Tali Garsiel合著的《浏览器的工作原理:现代网络浏览器的幕后》一文也非常有帮助。这篇文章发表于 2011 年,但其中许多关于浏览器工作原理的基本原理在撰写本文时仍然适用。
好的,我们开始吧。该过程可以分为以下主要阶段:
1.开始解析HTML
当浏览器开始通过网络接收页面的 HTML 数据时,它会立即设置其解析器来将 HTML 转换为文档对象模型 (DOM)。
文档对象模型 (DOM) 是构成 Web 上文档结构和内容的对象的数据表示。
解析过程的第一步是将 HTML 分解为表示开始标签、结束标签及其内容的标记。由此可以构建 DOM。
2. 获取外部资源
当解析器遇到外部资源(例如 CSS 或 JavaScript 文件)时,它会去获取这些文件。解析器会在 CSS 文件加载过程中继续运行,但会阻塞渲染,直到文件加载并解析完成(稍后会详细介绍)。
JavaScript 文件略有不同——默认情况下,它们会在JavaScript 文件加载和解析期间阻止HTML 解析。可以向脚本标签添加两个属性来缓解这种情况:defer
和async
。这两个属性都允许解析器在 JavaScript 文件在后台加载时继续运行,但它们的执行方式有所不同。稍后会详细介绍,但总结如下:
defer
表示文件的执行将被延迟,直到文档解析完成。如果多个文件具有 defer 属性,则它们将按照在 HTML 中被发现的顺序执行。
<script type="text/javascript" src="script.js" defer>
async
意味着文件在加载后就会执行,这可能是在解析过程期间或之后,因此无法保证异步脚本的执行顺序。
<script type="text/javascript" src="script.js" async>
预加载资源
顺便说一句,现代浏览器在被阻止时会继续扫描 HTML,并“预判”即将加载的外部资源,然后推测性地下载它们。不同浏览器执行此操作的方式有所不同,因此不能依赖其以特定方式运行。为了将资源标记为重要资源,从而更有可能在渲染过程的早期下载,可以使用带有rel="preload"的链接标签。
<link href="style.css" rel="preload" as="style" />
3. 解析 CSS 并构建 CSSOM
你之前可能听说过DOM,但你听说过CSSOM(CSS 对象模型)吗?在我之前研究这个主题之前,我完全没听说过!
CSS 对象模型 (CSSOM) 是所有 CSS 选择器及其相关属性的映射,以树状结构呈现,包含根节点、兄弟节点、后代节点、子节点以及其他关系。CSSOM 与文档对象模型 (DOM) 非常相似。它们都是关键渲染路径的一部分,关键渲染路径是正确渲染网站必须执行的一系列步骤。
CSSOM 与 DOM 一起构建渲染树,浏览器则使用渲染树来布局和绘制网页。
与 HTML 文件和 DOM 类似,CSS 文件在加载时必须被解析并转换为一棵树——也就是 CSSOM。它描述了页面上的所有 CSS 选择器、它们的层次结构及其属性。
CSSOM 与 DOM 的不同之处在于,它无法以增量方式构建,因为 CSS 规则会由于特殊性而在不同的点相互覆盖。这就是为什么 CSS 会阻止渲染,因为在所有 CSS 解析完成并构建 CSSOM 之前,浏览器无法知道每个元素在屏幕上的位置以及如何定位。
4.执行 JavaScript
JavaScript 资源的加载方式和时间将决定加载的具体时间,但最终它们会被解析、编译和执行。不同的浏览器使用不同的 JavaScript 引擎来执行此任务。解析 JavaScript 会占用大量的计算机资源,比其他类型的资源占用更多,因此优化 JavaScript 对于实现良好性能至关重要。阅读这篇精彩的文章,深入了解 JavaScript 引擎的工作原理。
加载事件
一旦同步加载的 JavaScript 和 DOM 完全解析并准备就绪,就会触发document.DOMContentLoaded事件。对于任何需要访问 DOM 的脚本(例如,以某种方式操作 DOM 或监听用户交互事件),最好先等待此事件,然后再执行脚本。
document.addEventListener('DOMContentLoaded', (event) => {
// You can now safely access the DOM
});
在异步 JavaScript、图像等其他所有内容都加载完成后,将触发window.load事件。
window.addEventListener('load', (event) => {
// The page has now fully loaded
});
5. 合并 DOM 和 CSSOM 构建渲染树
渲染树是 DOM 和 CSSOM 的组合,代表了将渲染到页面上的所有内容。这并不一定意味着渲染树中的所有节点都会以视觉形式呈现,例如,带有opacity: 0
或样式的节点visibility: hidden
将被包含,并且仍然可以被屏幕阅读器等读取,而设置为 的节点display: none
则不会被包含。此外,不包含任何视觉信息的标签(例如<head>
)将始终被忽略。
与 JavaScript 引擎一样,不同的浏览器有不同的渲染引擎。
6.计算布局并绘制
现在我们有了完整的渲染树,浏览器知道要渲染什么,但不知道渲染到哪里。因此,必须计算页面的布局(即每个节点的位置和大小)。渲染引擎会遍历渲染树,从顶部开始向下,计算每个节点应该显示的坐标。
完成后,最后一步是获取布局信息并将像素绘制到屏幕上。
瞧!完成这些之后,我们就有了一个完整渲染的网页!
文章来源:https://dev.to/starkiedev/how-the-browser-renders-a-web-page-1ahc