整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

看看浏览器如何解析我们写的页面?

述:浏览器渲染一共有五步

  • 处理 HTML 并构建 DOM 树。
  • 处理 CSS 构建 CSSOM 树。
  • 将 DOM 与 CSSOM 合并成一个渲染树。
  • 根据渲染树来布局,计算每个节点的位置。
  • 调用 GPU 绘制,合成图层,显示在屏幕上

具体如下图过程如下图所示

  • 在构建 CSSOM 树时,会阻塞渲染,直至 CSSOM 树构建完成。并且构建 CSSOM 树是一个十分消耗性能的过程,所以应该尽量保证层级扁平,减少过度层叠,越是具体的 CSS 选择器,执行速度越慢
  • 当 HTML 解析到 script 标签时,会暂停构建 DOM ,完成后才会从暂停的地方重新开始。也就是说,如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件。并且 CSS 也会影响 JS 的执行,只有当解析完样式表才会执行 JS ,所以也可以认为这种情况下, CSS 也会暂停构建 DOM

二、浏览器渲染五个阶段

2.1 第一步:解析HTML标签,构建DOM树

在这个阶段,引擎开始解析 html ,解析出来的结果会成为一棵 dom 树

dom 的目的至少有 2 个

getElementById

当解析器到达script标签的时候,发生下面四件事情

  1. html 解析器停止解析,
  2. 如果是外部脚本,就从外部网络获取脚本代码
  3. 将控制权交给 js 引擎,执行 js 代码
  4. 恢复 html 解析器的控制权

由此可以得到第一个结论1

  • 由于 <script> 标签是阻塞解析的,将脚本放在网页尾部会加速代码渲染。
  • defer 和 async 属性也能有助于加载外部脚本。
  • defer 使得脚本会在 dom 完整构建之后执行;
  • async 标签使得脚本只有在完全 available 才执行,并且是以非阻塞的方式进行的

2.2 第二步:解析CSS标签,构建CSSOM树

  • 我们已经看到 html 解析器碰到脚本后会做的事情,接下来我们看下 html 解析器碰到样式表会发生的情况
  • js 会阻塞解析,因为它会修改文档( document )。 css 不会修改文档的结构,如果这样的话,似乎看起来 css 样式不会阻塞浏览器 html 解析。但是事实上 css 样式表是阻塞的。阻塞是指当 cssom 树建立好之后才会进行下一步的解析渲染

通过以下手段可以减轻cssom带来的影响

  • 将 script 脚本放在页面底部
  • 尽可能快的加载 css 样式表
  • 将样式表按照 media type 和 media query 区分,这样有助于我们将 css 资源标记成非阻塞渲染的资源。
  • 非阻塞的资源还是会被浏览器下载,只是优先级较低

2.3 第三步:把DOM和CSSOM组合成渲染树(render tree)

2.4 第四步:在渲染树的基础上进行布局,计算每个节点的几何结构

布局( layout ):定位坐标和大小,是否换行,各种 position , overflow , z-index 属性

2.5 调用 GPU 绘制,合成图层,显示在屏幕上

将渲染树的各个节点绘制到屏幕上,这一步被称为绘制 painting

三、渲染优化相关

3.1 Load 和 DOMContentLoaded 区别

  • Load 事件触发代表页面中的 DOM , CSS , JS ,图片已经全部加载完毕。
  • DOMContentLoaded 事件触发代表初始的 HTML 被完全加载和解析,不需要等待 CSS , JS ,图片加载

3.2 图层

一般来说,可以把普通文档流看成一个图层。特定的属性可以生成一个新的图层。不同的图层渲染互不影响,所以对于某些频繁需要渲染的建议单独生成一个新图层,提高性能。但也不能生成过多的图层,会引起反作用。

通过以下几个常用属性可以生成新图层

  • 3D 变换: translate3d 、 translateZ
  • will-change
  • video 、 iframe 标签
  • 通过动画实现的 opacity 动画转换
  • position: fixed

3.3 重绘(Repaint)和回流(Reflow)

重绘和回流是渲染步骤中的一小节,但是这两个步骤对于性能影响很大

color

回流必定会发生重绘,重绘不一定会引发回流。回流所需的成本比重绘高的多,改变深层次的节点很可能导致父节点的一系列回流

以下几个动作可能会导致性能问题

window

很多人不知道的是,重绘和回流其实和 Event loop 有关

  • 当 Event loop 执行完 Microtasks 后,会判断 document 是否需要更新。因为浏览器是 60Hz 的刷新率,每 16ms 才会更新一次。
  • 然后判断是否有 resize 或者 scroll ,有的话会去触发事件,所以 resize 和 scroll 事件也是至少 16ms 才会触发一次,并且自带节流功能。
  • 判断是否触发了 media query
  • 更新动画并且发送事件
  • 判断是否有全屏操作事件
  • 执行 requestAnimationFrame 回调
  • 执行 IntersectionObserver 回调,该方法用于判断元素是否可见,可以用于懒加载上,但是兼容性不好
  • 更新界面
  • 以上就是一帧中可能会做的事情。如果在一帧中有空闲时间,就会去执行 requestIdleCallback 回调

3.4 减少重绘和回流

使用 translate 替代 top

  • 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)
  • 把 DOM 离线后修改,比如:先把 DOM 给 display:none (有一次 Reflow) ,然后你修改 100 次,然后再把它显示出来
  • 不要把 DOM 结点的属性值放在一个循环里当成循环里的变量
for(let i = 0; i < 1000; i++) {
 // 获取 offsetTop 会导致回流,因为需要去获取正确的值
 console.log(document.querySelector('.test').style.offsetTop)
}
  • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局
  • 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用 requestAnimationFrame
  • CSS 选择符从右往左匹配查找,避免 DOM 深度过深
  • 将频繁运行的动画变为图层,图层能够阻止该节点回流影响别的元素。比如对于 video 标签,浏览器会自动将该节点变为图层。

要:在本文中,将重点关注网页的初始渲染,即它从解析 HTML 开始。 我将探索可能导致高渲染时间的问题,以及如何解决它们。

本文分享自华为云社区《页面首屏渲染性能指南-云社区-华为云》,作者:Ocean2022。

我们知道渲染页面是一个将服务器的响应内容翻译成图片的过程。但是,如果你页面的渲染性能比较糟糕的话,可能会带来相对较高的跳出率。

在本文中,我将重点关注网页的初始渲染,即它从解析 HTML 开始。 我将探索可能导致高渲染时间的问题以及如何解决它们。

关键渲染路径(CRP)

关键渲染路径 (CRP) 是浏览器将代码转换为屏幕上可显示像素的过程。 它有几个阶段,其中一些可以并行执行以节省时间,但有些部分必须依次完成。 如下图所示:

首先,一旦浏览器得到响应,它就会开始解析它。 当它遇到依赖项时,它会尝试下载它。 如果它是一个样式表文件,浏览器必须在渲染页面之前完全解析它,这就是为什么 CSS 会阻塞渲染的原因。

如果是脚本,浏览器必须:停止解析,下载脚本,然后运行。 只有在那之后它才能继续解析,因为 JavaScript 程序可以改变网页的内容(尤其是 HTML)。 这就是为什么 JS 会阻塞解析的原因。

完成所有解析后,浏览器将构建文档对象模型 (DOM) 和级联样式表对象模型 (CSSOM)。 将它们组合在一起得到渲染树。 页面的不显示部分不会进入渲染树,因为它只包含绘制页面所需的数据。

倒数第二步是将渲染树进行布局, 这个阶段也称为回流:就是计算每个渲染树节点的每个位置及其大小的地方。

最后一步是绘制。 它会根据浏览器在前一阶段计算得到的数据对像素进行着色。

优化相关结论

因此,根据这一过程,我们在优化性能方面,得出了一些结论。如果你要提升页面初始化渲染的性能,你需要:

  • 减少传输的数据量
  • 减少浏览器必须下载的资源数量(尤其是阻塞的资源)
  • 减小 CRP 的长度

同时,我们会根据下面 3 个指标来衡量优化的效率:

  • FP(First Paint)
  • FCP(First Contentful Paint)
  • FMP(First Meaningful Paint)

除了渲染时间之外,还有其他一些因素也需要考虑。例如,你的页面使用了多少阻塞资源以及下载它们需要多长时间

性能优化策略

鉴于我们在上面得出的结论,我们得出网站性能优化有三种主要策略:

  1. 尽量减少通过网络传输的数据量;
  2. 减少通过网络传输的资源总数;
  3. 缩短关键渲染路径;

1. 减少要传输的数据量

首先,移除所有未使用的部分,例如 JavaScript 中无法访问的函数、带有从不匹配任何元素的选择器的样式以及被 CSS 永远隐藏的 HTML 标签。 其次,删除所有重复项。

然后,我建议建立一个自动压缩过程。 例如,它应该从你的后端服务中删除所有注释(但不是源代码)以及每个不包含附加信息的字符(例如 JS 中的空白字符)。

完成后,我们剩下的可以是文本字符串。 这意味着我们可以安全地应用诸如 GZIP(大多数浏览器都理解)之类的压缩算法。

最后,还有缓存。 浏览器第一次呈现页面时它不会有帮助,但它会在以后的访问中节省很多。 但是,记住两点至关重要:

  • 如果你使用 CDN,请确保支持缓存并在正确设置。
  • 与其等待资源的到期,不如 将文件的“指纹”嵌入到其 URL 中,以使本地缓存无效。

当然,应该为每个资源定义缓存策略。 有些可能很少改变或根本不会改变,有的则是变化的很快,还有些文件包含敏感的信息(可以使用 “private” 防止 CDN 缓存私有数据)

2. 减少关键资源的总数

“关键”仅指网页正确呈现所需的资源。 因此,我们可以直接跳过所有流程中没有涉及的样式以及脚本文件。

样式

为了告诉浏览器不需要特定的 CSS 文件,我们应该为所有引用样式表的链接设置媒体属性。 使用这种方法,浏览器将只根据需要处理与当前媒体(设备类型、屏幕尺寸)匹配的资源同时降低所有其他样式表的优先级。 例如,如果你将 media=“print” 属性添加到引用样式以打印页面的样式标记,则这些样式不会在不打印媒体时干扰你的关键渲染路径

为了进一步改进该过程,你还可以将一些样式内联这可以为我们节省了至少一次到服务器的往返行程

脚本

如上所述,脚本会阻塞解析,因为它们可以改变 DOM 和 CSSOM。 为了避免这一点,所有脚本标签都必须用属性标记——异步或延迟。

标有 async 的脚本不会阻塞 DOM 构建或 CSSOM,因为它们可以在 CSSOM 构建之前执行。 但请记住,内联脚本无论如何都会阻止 CSSOM,除非你将它们放在 CSS 之上。

相比之下,标有 defer 的脚本将在页面加载结束时进行执行

换句话说,使用 defer,脚本直到页面加载事件被触发后才会执行,而 async 让脚本在文档被解析时就会在后台运行。

3.缩短关键渲染路径长度

最后,应将 CRP 长度缩短到可能的最小值。

作为样式标签属性的媒体查询将减少必须下载的资源总数。 script 标签属性 defer 和 async 将防止相应的脚本阻塞解析。

使用 GZIP 压缩、压缩和归档资源将减少传输数据的大小(从而也减少数据传输时间)。

内联一些样式和脚本也可以减少浏览器和服务器之间的往返次数。

按照最新的最佳性能实践理念,一个网站应该做的最快的第一件事就是展示 ATF 内容。 ATF 代表首屏这是立即可见的区域,无需滚动。 因此,最好以首先加载所需样式和脚本的方式重新排列与渲染相关的所有内容,而其他所有内容都停止(既不解析也不渲染)。

结尾

总而言之,网站性能优化包含了网站响应的各个方面,例如缓存、设置 CDN、重构、资源优化等,但是所有这些都可以逐步完成。 作为 Web 开发人员,你可以将本文作为参考,并始终记住在实验之前和之后测量性能。

浏览器开发人员尽最大努力优化你访问的每个页面的网站性能,这就是浏览器通常实现所谓的“预加载器”的原因。 这部分程序会在你以 HTML 格式请求的资源之前进行扫描,以便一次发出多个请求并让它们并行运行。 这就是为什么在 HTML(逐行)以及脚本标签中保持样式标签彼此靠近的原因。

此外,尝试批量更新 HTML 以避免多个布局事件这些事件不仅由 DOM 或 CSSOM 中的更改触发,而且在设备方向更改和窗口大小调整时也会触发。

点击下方,第一时间了解华为云新鲜技术~

华为云博客_大数据博客_AI博客_云计算博客_开发者中心-华为云

览器解析HTML文件的过程是网页呈现的关键步骤之一。具体介绍如下:


HTML文档的接收和预处理

  1. 网络请求处理:当用户输入URL或点击链接时,浏览器发起HTTP请求,服务器响应并返回HTML文件。此过程中,浏览器需要处理DNS查询、建立TCP连接等底层网络通信操作。
  2. 预解析优化:为了提高性能,现代浏览器在主线程解析HTML之前会启动一个预解析线程,提前下载HTML中链接的外部CSS和JS文件。这一步骤确保了后续渲染过程的顺畅进行。

解析为DOM树

  1. 词法分析和句法分析:浏览器的HTML解析器通过词法分析将HTML文本标记转化为符号序列,然后通过句法分析器按照HTML规范构建出DOM树。每个节点代表一个HTML元素,形成了多层次的树状结构。
  2. 生成对象接口:生成的DOM树是页面元素的结构化表示,提供了操作页面元素的接口,如JavaScript可以通过DOM API来动态修改页面内容和结构。

CSS解析与CSSOM树构建

  1. CSS文件加载与解析:浏览器解析HTML文件中的<link>标签引入的外部CSS文件和<style>标签中的内联CSS,生成CSSOM树。CSSOM树反映了CSS样式的层级和继承关系。
  2. CSS属性计算:包括层叠、继承等,确保每个元素对应的样式能够被准确计算。这些计算过程为后续的布局提供必要的样式信息。

JavaScript加载与执行

  1. 阻塞式加载:当解析器遇到<script>标签时,它会停止HTML的解析,转而先加载并执行JavaScript代码。这是因为JS可能会修改DOM结构或CSSOM树,从而影响已解析的部分。
  2. 异步和延迟加载:为了不影响页面的初步渲染,可以采用async或defer属性来异步加载JS文件,这样可以在后台进行JS的加载和执行,而不阻塞HTML解析。

渲染树的构建

  1. 合并DOM树和CSSOM树:有了DOM树和CSSOM树后,浏览器将它们组合成渲染树,这个树只包含显示界面所需的DOM节点及对应的样式信息。
  2. 不可见元素的排除:渲染树会忽略例如<head>、<meta>等不可见元素,只关注<body>内的可视化内容。

布局计算(Layout)

  1. 元素位置和尺寸确定:浏览器从渲染树根节点开始,递归地计算每个节点的精确位置和尺寸,这个过程也被称为“回流”或“重排”,是后续绘制的基础。
  2. 布局过程的优化:现代浏览器会尽量优化布局过程,例如通过流式布局的方式减少重复计算,确保高效地完成布局任务。

绘制(Paint)

  1. 像素级绘制:绘制是一个将布局计算后的各元素绘制成像素点的过程。这包括文本、颜色、边框、阴影以及替换元素的绘制。
  2. 层次化的绘制:为了高效地更新局部内容,浏览器会将页面分成若干层次(Layer),对每一层分别进行绘制,这样只需更新变化的部分。

因此,我们开发中要注意以下几点:

  • 避免过度使用全局脚本:尽量减少使用全局脚本或者将它们放在文档底部,以减少对HTML解析的阻塞。
  • 合理组织CSS和使用CSS预处理器:合理组织CSS文件的结构和覆盖规则,利用CSS预处理器进行模块化管理。
  • 利用浏览器缓存机制:通过设置合理的缓存策略,减少重复加载相同资源,提升二次访问的体验。
  • 优化图片和多媒体资源:适当压缩图片和优化多媒体资源的加载,减少网络传输时间和渲染负担。

综上所述,浏览器解析HTML文件是一个复杂而高度优化的过程,涉及从网络获取HTML文档到最终将其渲染到屏幕上的多个步骤。开发者需要深入理解这些步骤,以优化网页性能和用户体验。通过合理组织HTML结构、优化资源加载顺序、减少不必要的DOM操作和合理安排CSS和JavaScript的加载与执行,可以显著提升页面加载速度和运行效率。