Skip to main content

导航中会发生什么

当我们在浏览器中输入网址,浏览器从互联网提取数据并显示网页,这中间都发生了什么?

第 1 阶段,处理用户输入

处理用户输入

当用户在地址栏中输入内容时,界面线程首先判断这是搜索关键字还是URL

  • 如果是网址,则进入导航流程
  • 如果是关键词,则交给默认搜索引擎处理

第 2 阶段,检查 Service Worker 和缓存

浏览器正式发起请求前,会优先进行以下检查:

  1. Service Worker 是否注册并拦截请求(若有,自定义响应或继续请求)
  2. 强缓存(Cache-Control) 是否命中(若命中,直接使用缓存资源)

这一步可以完全跳过网络请求,是性能优化的第一道防线

第 3 阶段,网络请求过程

若没有被拦截或缓存命中,则进入网络请求流程:

  1. DNS 解析:将域名解析为 IP 地址(可能走本地缓存)
  2. TCP 连接建立:三次握手;若是 HTTPS,还要进行 TLS 握手
  3. 发送 HTTP 请求:包括请求头、Cookie、User-Agent 等
  4. 服务器返回响应:可能是 200 成功,也可能是 301/302 重定向(会重新发起请求)

第 4 阶段,响应处理与渲染

渲染程序的核心工作是将 HTML、CSS 和 JavaScript 转换为用户可以与之互动的网页,浏览器渲染是一个流水线式的过程。

解析DOM

  1. HTML 解析和 DOM 构建:将 HTML 转化为 DOM,DOM 是浏览器对网页的内部表示
  • 子资源加载:如果 HTML 文档中存在 <img><link> 等内容,预加载扫描器会查看 HTML 解析器生成的令牌,并向浏览器进程中的网络线程发送请求。
  • JavaScript 阻止解析:当 HTML 解析器遇到 <script> 标记时,它会暂停解析 HTML 文档,并必须加载、解析和执行 JavaScript 代码,这个过程可能引发的回流与重绘,这是性能优化的重要环节
  • 资源优先级:<link rel="preload"> 可以告知浏览器需要尽快下载该资源。

JavaScript 可以阻止解析

当 HTML 解析器发现一个 <script> 标签时,它会暂停对 HTML 文档的解析,并加载、解析和执行 JavaScript 代码。因为 JavaScript 可以使用诸如 document.write() 之类的功能来改变文档的结构,从而改变整个 DOM 结构,因此 HTML 解析器必须等待 JavaScript 运行完毕,然后才能恢复对 HTML 文档的解析。为了提高页面加载性能,通常把 <script> 标签放在 <body> 标签的底部。

如果不希望阻塞页面加载,也可以使用 <script> 标签的 async 和 defer 属性。

  • async 表示异步加载和执行,不会阻塞页面的呈现
  • defer 表示将在 HTML 文档解析完成后执行

样式计算

  1. CSS 解析和样式计算:解析 CSS 并确定每个 DOM 节点的样式

布局

  1. 布局:主线程会遍历 DOM 和计算样式,并创建包含 x、y 坐标和边界框大小等信息的布局树
  • 如果应用了 display: none,则该元素不属于布局树,具有 visibility: hidden 的元素属于布局树

绘制

  1. 绘制:知道元素的大小、形状和位置,但仍需要判断绘制它们的顺序
  • 主线程会遍历布局树以创建绘制记录,先绘制背景,然后绘制文本,最后绘制矩形

合成

  1. 合成与光栅化:将网页的各个部分拆分为图层、单独光栅化这些图层,并在合成程序线程的单独线程中将其合成为网页
  • 为了确定哪些元素需要位于哪些层中,主线程会遍历布局树以创建层树
  • 如果网页的某些部分应为单独的层(例如滑入式侧边菜单),但未获得单独的层,则您可以在 CSS 中使用 will-change 属性向浏览器发出提示,开发中常通过 will-changetransform 强制触发独立图层

光栅

光栅化就是把矢量图形转换成一堆像素点,光栅线程会对每个图块进行光栅化处理,并将其存储在 GPU 显存中

交互优化

页面滚动性能优化:所有 touchstarttouchmove 监听器都应加 { passive: true },可以避免阻塞滚动,保持合成器线程的流畅性。

高频事件监听器(如画板):使用 requestAnimationFrame 合并更新 + getCoalescedEvents 提高精度。

document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});

单页应用大量事件委托:避免全页面绑定事件 + preventDefault,否则整个网页都会被标记为非快速滚动区域。

页面首屏加载优化:使用功能策略防止阻塞脚本(如 sync-script: none)。

参考文章

https://developer.chrome.com/blog/inside-browser-part2/

https://developer.chrome.com/blog/inside-browser-part3/

https://developer.chrome.com/blog/inside-browser-part4/