前端实践-原理-浏览器解析 | 豆包MarsCode AI 刷题

4 阅读4分钟

前言

在前端开发中,理解浏览器如何解析和渲染页面,不仅能帮助我们优化代码性能,还能更好地设计用户体验。本篇文章将以浏览器解析为核心,分阶段解析浏览器从接收到 HTML 到最终呈现页面的全过程。

当浏览器从网络线程接收到 HTML 文档后,会启动一个渲染任务,并将其传递给渲染主线程的消息队列。在事件循环机制的作用下,渲染主线程会取出任务,开启渲染流程。

具体流程

1. HTML 解析

浏览器解析 HTML 的过程是从接收到 HTML 文档后逐行解析。当遇到外部 CSS 和 JS 时,会分别处理:

  • CSS:CSS 的解析由预解析线程负责,不会阻塞 HTML 的解析。
  • JS:如果遇到 script 标签,会暂停 HTML 的解析,等待 JS 下载完成后再执行。这是因为 JS 可能会动态修改 DOM 树。

输出:HTML 解析完成后,生成了 DOM 树和 CSSOM 树。

实践提示:将 <script> 标签放在页面底部,或使用 async/defer 属性,减少 JS 对页面解析的阻塞。

2. 样式计算

此阶段,浏览器会遍历 DOM 树的每个节点,计算其最终样式(Computed Style)。在计算过程中会完成以下任务:

  • 将 CSS 的相对单位(如 em%)转换为绝对值(如 px)。
  • 将关键字(如 inheritinitial)解析为具体的样式值。

输出:生成一棵带有样式的 DOM 树。

实践提示:尽量减少复杂的继承和层叠规则,以加快样式计算的速度。

3. 布局计算

布局计算会遍历带有样式的 DOM 树,为每个节点计算几何信息,如宽高和位置。这一阶段会生成布局树

  • 过滤无效节点:例如 display: none 的元素不会进入布局树。
  • 添加伪元素:伪元素(如 ::before::after)会加入布局树。
  • 计算几何值:例如,宽高从父元素中继承。

输出:生成完整的布局树。

实践提示:避免频繁操作 DOM 的宽高属性,因为每次获取都会触发重新布局(Reflow)。

4. 分层处理

布局树生成后,浏览器会对其进行分层,将其拆分为多个图层(Layer)。 一些样式属性(如 z-indexposition: fixed)或伪类(如 will-change)会触发额外分层。

好处:分层使得局部更新无需重绘整个页面。

输出:生成图层树。

实践提示:使用 will-change 提示浏览器哪些属性可能发生改变,以优化分层。

5. 绘制阶段

浏览器将每个图层的绘制指令集独立生成,用于描述图层内容的绘制方式。

输出:生成绘制指令。

6. 分块与光栅化

为了提高效率,浏览器会将每个图层分成多个小块,并在 GPU 进程中进行光栅化,将其转换为位图。 此阶段优先处理靠近视口的块,确保用户的可见区域优先渲染。

输出:位图数据。

7. 最终绘制

合成线程将位图传递给 GPU,生成最终的屏幕渲染。此阶段会考虑变形(如缩放、旋转)并进行屏幕上的内容合成。

性能优化与开发实践

1. 减少 Reflow 和 Repaint

  • 什么是 Reflow? Reflow 是重新计算布局树的过程,通常由布局属性的更改触发。它是一种代价昂贵的操作。 示例:修改宽高、字体大小、盒模型属性等。
  • 什么是 Repaint? Repaint 是重新生成绘制指令的过程,但不涉及布局树。通常由非布局属性的更改触发。 示例:修改背景色、文字颜色等。

实践提示:使用 transformopacity 进行动画效果,而不是直接修改布局属性。

2. 优化动画性能

  • 使用 transformopacity 创建高效动画。
  • 这些操作只涉及合成线程,不会触发重绘或重新布局。

3. 避免不必要的 DOM 操作

  • 合并 DOM 操作,使用文档片段(DocumentFragment)减少 DOM 更新次数。

4. 使用异步加载资源

  • 将 CSS 文件放在 <head> 中,确保尽早加载。
  • 将 JS 文件放在页面底部,并使用 asyncdefer 属性。