前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • Vue

    • 从源码解读Vue生命周期
    • 组件的本质
    • 有状态组件的设计
    • 设计 VNode
    • 辅助创建 VNode 的 h 函数
    • 自定义渲染器和异步渲染
    • 渲染器之挂载
    • 渲染器的核心 Diff 算法
    • 渲染器之patch
    • 图解 Vue 响应式原理
    • 图解 Vue 异步更新
    • 剖析 Vue 内部运行机制
    • vue响应式原理模拟
    • vue状态管理之vuex
    • 理解Vue的设计思想及实现Vue
    • diff算法深入
    • vue router vuex原理分析
    • Vue3初探响应式原理.
    • vue2源码分析
    • vue组件化实践
  • React

  • Webpack

  • Node

  • Javascript

  • 综合

完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

深入剖析浏览器中页面的渲染过程|原理篇

文章学习导航
面试总览

30 秒速记

  • 核心判断:页面从 HTML/CSS 解析到布局、绘制和合成是一条流水线,变更命中哪一层决定更新成本
  • 原理主线:围绕 「浏览器的内部结构」、「Chrome 多进程架构」、「浏览器中页面的渲染过程」 建立输入、状态变化与输出之间的因果关系
  • 文章范围:解析了浏览器页面渲染的完整流程,包括浏览器的内部结构、Chrome多进程架构、各子系统与线程的协作,以及从输入URL到页面显示的关键步骤,帮助前端开发者深入理解页面渲染原理与性能优化要点。
  • 边界与代价:浏览器会批量延迟样式计算,但同步读取布局可能强制刷新;合成层也不是越多越好
  • 工程落地:优化应使用性能面板确认主线程、布局、绘制与合成瓶颈,再减少对应阶段的工作量

页面渲染是一条从解析、布局、绘制到光栅化与合成的流水线,样式变更命中哪一环,决定了更新成本。 浏览器解析 HTML 和 CSS 得到页面结构与样式,再计算元素几何信息和绘制顺序,最后把结果转换成像素显示。尺寸或位置变化通常会触发布局和重绘,只有颜色变化时一般不必重新计算几何信息。合成层可以由合成器线程处理移动,但分层也应按实际渲染表现验证。

这篇文章不要按 API 清单来背。先用上面的 Mind Map 建立全局结构,再通过交互 DEMO 观察正常路径和边界路径如何改变状态;阅读正文时重点核对每一步的输入、负责执行的参与者、产生的中间状态以及最终可观察结果。遇到版本敏感结论,要把“历史实现”“当前行为”和“工程兼容策略”分开说明;遇到性能或架构取舍,则用实际指标、失败现象和验证手段支撑判断。

版本校准: 原理文章中的代码代表特定实现与写作时间。应用到当前项目时,应先确认浏览器、框架或工具的主版本,再区分稳定的规范语义、可变化的内部实现和项目自身约束。

# 浏览器的内部结构

从结构上来说,浏览器主要包括了八个子系统:用户界面、浏览器引擎、渲染引擎、网络子系统、JavaScript 解释器、XML 解析器、显示后端、数据持久性子系统

这些子系统组合构成了我们的浏览器。页面的加载和渲染过程,离不开网络子系统、渲染引擎、JavaScript 解释器和浏览器引擎。

以前端开发最常使用的 Chrome 浏览器为例, Chrome 浏览器是使用多进程架构的方式来管理这些子系统。

# Chrome 多进程架构

Chrome 浏览器采用的多进程架构,主要包括四个进程

  • 浏览器进程:选项卡之外的所有内容都由浏览器进程处理,浏览器进程则主要用于控制和处理用户可见的 UI 部分(包括地址栏,书签,后退和前进按钮)和用户不可见的隐藏部分(例如网络请求和文件访问)
  • GPU 进程:该进程用于完成图像处理任务,同时还支持分解成多个进程进行处理
  • 渲染器进程:Chrome 浏览器中支持多个选项卡,其中每个选项卡在单独的渲染器进程中运行,渲染器进程主要用于控制和处理选项卡中的网站内容显示
  • 插件进程:管理 Chrome 浏览器中的各个插件

对于“在浏览器的地址栏中输入 URL,按下回车键,到浏览器渲染页面”这个过程,浏览器内部会通过浏览器进程和渲染器进程,进行很多交互逻辑,最终才得以将页面内容显示在屏幕上

其中,浏览器进程和渲染器进程同样支持多线程,包括以下这些线程。

这些线程其实并不陌生,在前面介绍的内容中有提到,比如:

  • 在页面的加载过程中,涉及 GUI 渲染线程与 JavaScript 引擎线程间的互斥关系,因此页面中的<script>和<style>元素设计不合理会影响页面加载速度;
  • 在 UI 线程、网络线程、存储线程、浏览器事件触发线程、浏览器定时器触发线程中,I/O 事件通过异步任务完成时触发的函数回调,解决了单线程的 Javascript 阻塞问题。

下面我们再来看下 Chrome 浏览器中页面的渲染过程,包括浏览器进程和线程如何通信来显示页面。

# 浏览器中页面的渲染过程

首先我们将浏览器中页面的渲染过程分为两部分。

  • 页面导航:用户输入 URL,浏览器进程进行请求和准备处理。
  • 页面渲染:获取到相关资源后,渲染器进程负责选项卡内部的渲染处理。

# 1. 页面导航过程

当用户在地址栏中输入内容时,浏览器内部会进行以下处理。

  • 首先浏览器进程的 UI 线程会进行处理:如果是 URI,则会发起网络请求来获取网站内容;如果不是,则进入搜索引擎。
  • 如果需要发起网络请求,请求过程由网络线程来完成。HTTP 请求响应如果是 HTML 文件,则将数据传递到渲染器进程;如果是其他文件则意味着这是下载请求,此时会将数据传递到下载管理器。
  • 如果请求响应为 HTML 内容,此时浏览器应导航到请求站点,网络线程便通知 UI 线程数据准备就绪。
  • 接下来,UI 线程会寻找一个渲染器进程来进行网页渲染。当数据和渲染器进程都准备好后,HTML 数据通过 IPC 从浏览器进程传递到渲染器进程中。
  • 渲染器进程接收 HTML 数据后,将开始加载资源并渲染页面。
  • 渲染器进程完成渲染后,通过 IPC 通知浏览器进程页面已加载。

以上是用户在地址栏输入网站地址,到页面开始渲染的整体过程。为了方便理解,我帮你梳理了一个流程图:

如果当前页面跳转到其他网站,浏览器将调用一个单独的渲染进程来处理新导航,同时保留当前渲染进程来处理像unload这类事件。

在上面的过程中可以看到,页面导航主要依赖浏览器进程。其中,上述过程中的步骤 5 便是页面的渲染部分,该过程同样依赖渲染器进程,我们一起来看看

# 2. 页面渲染过程

前面说过,渲染器进程负责选项卡内部发生的所有事情,它的核心工作是将 HTML、CSS 和 JavaScript 转换为可交互的页面。

整体上,渲染器进程渲染页面的流程基本如下。

  • 解析(Parser):解析 HTML/CSS/JavaScript 代码。
  • 布局(Layout):定位坐标和大小、是否换行、各种position/overflow/z-index属性等计算。
  • 绘制(Paint):判断元素渲染层级顺序。
  • 光栅化(Raster):将计算后的信息转换为屏幕上的像素。

1. 解析

渲染器进程的主线程会解析以下内容:

  • 解析 HTML 内容,产生一个 DOM 节点树;
  • 解析 CSS,产生 CSS 规则树;
  • 解析 Javascript 脚本,由于 Javascript 脚本可以通过 DOM API 和 CSSOM API 来操作 DOM 节点树和 CSS 规则树,因此该过程中会等待 JavaScript 运行完成才继续解析 HTML

解析完成后,我们得到了 DOM 节点树和 CSS 规则树,布局过程便是通过 DOM 节点树和 CSS 规则树来构造渲染树(Render Tree)

2. 布局

通过解析之后,渲染器进程知道每个节点的结构和样式,但如果需要渲染页面,浏览器还需要进行布局,布局过程便是我们常说的渲染树的创建过程。

在这个过程中,像header或display:none的元素,它们会存在 DOM 节点树中,但不会被添加到渲染树里。

布局完成后,将会进入绘制环节

3. 绘制

在绘制步骤中,渲染器主线程会遍历渲染树来创建绘制记录。

需要注意的是,如果渲染树发生了改变,则渲染器会触发重绘(Repaint)和重排(Reflow)。

  • 重绘:屏幕的一部分要重画,比如某个 CSS 的背景色变了,但是元素的几何尺寸没有变。
  • 重排:元素的几何尺寸变了(渲染树的一部分或全部发生了变化),需要重新验证并计算渲染树。

为了不对每个小的变化都进行完整的布局计算,渲染器会将更改的元素和它的子元素进行脏位标记,表示该元素需要重新布局。其中,全局样式更改会触发全局布局,部分样式或元素更改会触发增量布局,增量布局是异步完成的,全局布局则会同步触发。

重排需要涉及变更的所有的结点几何尺寸和位置,成本比重绘的成本高得多的多。所以我们要注意以避免频繁地进行增加、删除、修改 DOM 结点、移动 DOM 的位置、Resize 窗口、滚动等操作,因为这些操作可能会导致性能降低。

4. 光栅化

通过解析、布局和绘制过程,浏览器获得了文档的结构、每个元素的样式、绘制顺序等信息。将这些信息转换为屏幕上的像素,这个过程被称为光栅化

光栅化可以被 GPU 加速,光栅化后的位图会被存储在 GPU 内存中。根据前面介绍的渲染流程,当页面布局变更了会触发重排和重绘,还需要重新进行光栅化。此时如果页面中有动画,则主线程中过多的计算任务很可能会影响动画的性能

因此,现代的浏览器通常使用合成的方式,将页面的各个部分分成若干层,分别对其进行栅格化(将它们分割成了不同的瓦片),并通过合成器线程进行页面的合成

合成过程如下:

  • 当主线程创建了合成层并确定了绘制顺序,便将这些信息提交给合成线程;
  • 合成器线程将每个图层栅格化,然后将每个图块发送给光栅线程
  • 光栅线程栅格化每个瓦片,并将它们存储在 GPU 内存中
  • 合成器线程通过 IPC 提交给浏览器进程,这些合成器帧被发送到 GPU 进程处理,并显示在屏幕上。

合成的真正目的是,在移动合成层的时候不用重新光栅化。因为有了合成器线程,页面才可以独立于主线程进行流畅的滚动

我们在绘制页面的时候,也可能会遇到很多奇怪的渲染问题,比如使用了transform:scale可能会导致某些浏览器中渲染模糊,究其原因则是由于光栅化过程导致的

面试官追问

追问 1一个节点在 DOM 中能查到,但页面上始终看不见,同事断言是光栅化失败;已知它设置了 display:none,你怎么反驳并定位它消失在哪个阶段?
参考回答

它更可能在布局阶段就没有进入渲染树,而不是到了光栅化阶段才丢失。先核对 DOM 与 CSS 规则,再检查计算样式;若确为 display:none,后续不会为它生成正常的布局、绘制和像素结果,但还要排除样式覆盖等原因。

追问 2一个长列表需要连续修改上百个节点的尺寸和位置,产品要求减少卡顿;你会怎样组织更新,避免每改一个节点都扩大布局成本?
参考回答

我会集中写入 DOM 与样式,避免在修改过程中交替读取几何信息,并尽量把变更限制在可隔离的子树。浏览器会对需要重新布局的元素及其子元素做脏标记,但全局样式变化仍可能触发同步的全局布局,批处理并不保证没有重排。

追问 3同一段位移动画把 top 改成合成层上的移动后更流畅,前端同事因此主张所有元素都强制分层;你会如何评估这个选型?
参考回答

移动已有合成层时可能绕过重新光栅化,并由合成器线程完成合成,因此能降低主线程压力。但分层后仍需分别栅格化,位图还会占用 GPU 内存;是否建层应结合动画范围和实际渲染记录判断,不能把分层当成无成本优化。

追问 4线上动画只在复杂页面掉帧,颜色变化正常、尺寸变化明显卡顿;你如何沿着渲染流水线找到首个高成本阶段?
参考回答

我会先区分仅改变颜色的重绘与改变几何尺寸的重排,再检查变化是否继续引发绘制和光栅化。尺寸变化需要重新验证相关节点的位置和大小,成本通常高于单纯重绘;若主线程同时执行大量计算,还要确认动画是否被这些任务阻塞。

追问 5设计稿中的卡片使用 transform:scale 后文字偶发模糊,视觉要求改回改变宽高,性能负责人反对;你如何解释并取舍?
参考回答

模糊可能来自图层按既有位图缩放后的光栅化表现,不能仅凭现象认定是字体或 CSS 计算错误。改变宽高可能重新触发布局、绘制与光栅化,而 transform 移动或缩放可能更多依赖合成;应分别验证清晰度与主线程代价,再决定是否接受重排成本。

← 小程序原理一个网络请求是怎么进行的 →

fe
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片NEW
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 业务场景题真实业务问题与追问
  • 查漏补缺常见问题解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
    • AI 定制路线NEW按你的简历现排
    • AI 知识地图NEW串起全站知识点
  • 动态
    • AI 热点NEWAI 每日动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
AI 助手NEW
旧版
  • Vue

    • 从源码解读Vue生命周期
    • 组件的本质
    • 有状态组件的设计
    • 设计 VNode
    • 辅助创建 VNode 的 h 函数
    • 自定义渲染器和异步渲染
    • 渲染器之挂载
    • 渲染器的核心 Diff 算法
    • 渲染器之patch
    • 图解 Vue 响应式原理
    • 图解 Vue 异步更新
    • 剖析 Vue 内部运行机制
    • vue响应式原理模拟
    • vue状态管理之vuex
    • 理解Vue的设计思想及实现Vue
    • diff算法深入
    • vue router vuex原理分析
    • Vue3初探响应式原理.
    • vue2源码分析
    • vue组件化实践
  • React

  • Webpack

  • Node

  • Javascript

  • 综合