从输入url到页面呈现的过程,浏览器究竟干了什么🤔?

66 阅读3分钟

浏览器完整渲染流程

以单页应用(SPA)为例,从输入URL到首屏展现,其核心流程可以清晰地划分为几个关键阶段。下面我们逐步拆解,看看浏览器在每个阶段都做了什么。


阶段 1:加载 HTML 和初步解析

  1. 输入 URL:浏览器请求 index.html(若为 SPA 的history模式,服务器需配置所有路径返回该文件)。
  2. 返回 HTML:服务器返回 index.html(可能包含内联 CSS/JS 或外链资源)。
  3. 创建渲染进程:浏览器分配渲染进程(同源页面可能复用)。

阶段 2:解析 HTML,构建 DOM 和 CSSOM

  1. 解析 HTML(Blink 引擎):
  • 遇到 <link rel="stylesheet">:异步下载 CSS,但会阻塞渲染树的构建(必须等 CSSOM 完成)。
  • 遇到 <script>
    • 无属性:同步下载并执行,阻塞 DOM 解析。
    • defer:异步下载,在 DOM 解析完成后、DOMContentLoaded 前按顺序执行(Vue 默认行为)。
    • async:异步下载,下载完立即执行(顺序不确定)。
  • 遇到 <img>:异步加载,不阻塞解析。
  1. 构建 DOM 树:逐步生成 DOM 节点(此时 Vue 的 #app 只是一个空 div)。
  2. 构建 CSSOM 树:同步解析内联 <style> 或下载完成的 CSS 文件。

阶段 3:触发 DOMContentLoaded 事件

  1. 触发条件:
  • DOM 树构建完成(所有同步 HTML 解析完毕)。
  • 所有同步 JS(无 async/defer)和 defer JS 执行完毕。
  • CSSOM 构建完成(即使 CSS 是异步加载的,也必须等它解析完)。
  1. 此时状态:
  • 浏览器已掌握完整的 DOM 和 CSSOM,可以构建渲染树。
  • Vue 项目:deferapp.js 已执行,Vue 完成挂载,虚拟 DOM 转为真实 DOM。

阶段 4:渲染页面(Render Tree → 绘制)

  1. 合并 DOM + CSSOM:生成渲染树(Render Tree),排除 display: none 的元素。
  2. 布局(Layout):计算每个节点的尺寸和位置(如 width: 50% 转为实际像素)。
  3. 绘制(Paint):将布局结果转为屏幕像素(可能触发重排/重绘)。
  4. 合成(Composite):GPU 加速渲染图层(如 transform 动画)。

✅ 用户此时看到首屏内容(即使图片或异步资源未加载完)。


阶段 5:触发 load 事件

  1. 触发条件:所有资源(图片、字体、异步脚本、iframe 等)加载完成。
  2. 关键区别:
  • DOMContentLoaded:DOM + CSSOM 就绪,页面已渲染。
  • load:所有资源就绪,不重新渲染,仅标志“完全加载”。

阶段 6:Vue 路由切换(SPA 特性)

  1. 用户点击链接:Vue Router 拦截导航,不刷新页面。
  2. 异步加载组件:若配置了懒加载(如 () => import('./User.vue')),请求对应 JS 文件。
  3. 局部更新 DOM:Vue 通过虚拟 DOM diff 更新页面,无需重新渲染整个文档。

关键概念总结

事件/阶段触发条件Vue 项目的表现
HTML 解析下载并解析 index.html遇到 #app 空容器
DOMContentLoadedDOM + CSSOM 就绪,defer JS 执行完Vue 完成挂载,首屏显示
渲染树 → 绘制合并 DOM + CSSOM,布局,绘制用户看到内容(可能缺图片)
load所有资源加载完成图片/字体等加载完毕,不重新渲染
路由切换用户交互触发导航异步加载组件,局部更新 DOM

常见误区澄清

  1. 误区:load 事件后会重新渲染页面。 真相:渲染早在 DOMContentLoaded 后完成,load 只是资源加载完成的回调。
  2. 误区:Vue 首屏一定是空 div。 真相:SSR/预渲染/骨架屏可优化首屏内容。

最终流程图

image.png

这样梳理后,是否完全理清了浏览器的渲染机制? 😊