---
layout: "@/layouts/BlogPostLayout.astro"
title: "浏览器的运行原理"
description: "本文详细介绍了浏览器渲染页面的七个阶段（HTML解析、样式计算、布局、分层、绘制、分块、光栅化）以及reflow（回流）、repaint（重绘）的概念和transform效率高的原因。"
date: "2023-08-10"
image: "https://images.unsplash.com/photo-1510511459019-5dda7724fd87?q=80&w=1470&auto=format&fit=crop"
tags: ["浏览器", "前端", "渲染原理"]
tagId: ["browser", "frontend", "rendering"]
category: "前端开发"
categoryId: "frontend"
readTime: "7 min read"
---

浏览器渲染页面的过程可以分为七个阶段：

### 1. HTML解析

浏览器接收到服务器响应的HTML文档后，会首先解析HTML文档，构建DOM树。DOM树是由DOM元素及属性节点组成的，树的根是document对象。

### 2. 样式计算

浏览器会解析CSS文件和元素上的inline样式，计算出DOM节点的样式。这个过程包括：

- 把CSS转换为浏览器能够理解的结构
- 计算出DOM节点的具体样式

### 3. 布局

布局阶段会计算出DOM树中可见元素的几何位置，这个过程叫做布局或重排。

### 4. 分层

浏览器会根据布局后的DOM树，将其分为多个图层。这个过程主要是为了处理页面中的复杂效果，如3D变换、页面滚动等。

### 5. 绘制

在分层的基础上，浏览器会为每个图层生成绘制列表，并将其提交到合成线程。

### 6. 分块

合成线程会将图层划分为图块，这些图块的大小通常是256x256或者512x512。

### 7. 光栅化

合成线程会按照视口附近的图块来优先生成位图，这个过程叫做光栅化。光栅化过程中，会将图块转换为位图。

## 回流（Reflow）和重绘（Repaint）

### 回流（Reflow）

当我们对DOM的修改引发了DOM几何尺寸的变化（比如修改元素的宽、高或隐藏元素等）时，浏览器需要重新计算元素的几何属性，然后再将计算的结果绘制出来。这个过程就是回流（也叫重排）。

引起回流的操作包括：

- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化（文字数量或图片大小等等）
- 元素字体大小变化
- 添加或者删除可见的DOM元素
- 激活CSS伪类（例如：:hover）
- 查询某些属性或调用某些方法

一些常用且会导致回流的属性和方法：

- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- scrollIntoView()、scrollIntoViewIfNeeded()
- getComputedStyle()
- getBoundingClientRect()
- scrollTo()

### 重绘（Repaint）

当我们对DOM的修改导致了样式的变化、但未影响其几何属性（比如修改了颜色或背景色）时，浏览器不需要重新计算元素的几何属性、直接为该元素绘制新的样式。这个过程叫做重绘。

相比回流，重绘的性能开销较小，因为重绘只是重新绘制元素的外观，而不需要重新计算元素的位置。

## 为什么transform效率高

transform是通过创建一个新的复合图层来实现的，这个复合图层会单独进行绘制，然后再与其他图层进行合成。这样，当我们使用transform进行变换时，只需要重新绘制这个复合图层，而不需要重新绘制整个页面。

使用transform可以避免回流和重绘，因为它不会影响到DOM的布局，只会影响到最终的渲染结果。这就是为什么使用transform进行动画会比直接修改元素的top、left等属性效率高的原因。

## 优化策略

为了减少回流和重绘，我们可以采取以下策略：

1. **批量修改DOM**：使用DocumentFragment或者先将元素设为display: none，然后进行多次修改，最后再显示。

2. **避免频繁读取会引发回流/重绘的属性**：如果必须多次读取，就将值缓存起来。

3. **使用transform和opacity来实现动画效果**：这两个属性不会触发回流。

4. **使用will-change属性**：这个属性可以提前告知浏览器元素将要发生的变化，浏览器可以提前做好优化工作。

5. **使用绝对定位或固定定位**：绝对定位和固定定位的元素发生变化时，只会影响自身，不会影响其他元素。

通过理解浏览器的渲染原理和优化策略，我们可以编写出更高效的前端代码，提升用户体验。