Chrome 浏览器原理之 Navigation

2,108 阅读5分钟

原文:developers.google.com/web/updates…

该部分导航(Navigation)旨在阐述:当用户在浏览器地址栏输入一个 URL,到浏览器呈现网页之前,各个进程和线程之间是如何交互的。

一些概念

Chrome 多进程架构

chrome 采用多进程架构,最顶层的是浏览器进程,与负责应用其它部分的进程协调。打开 更多工具-》任务管理器:

其中:

  • 浏览器进程 (browser process):控制应用程序的 "chrome" 部分,包括地址栏、书签、前进/后退按钮
  • 渲染进程(renderer process):控制 tab 内的所有内容,图中标签页
  • 插件进程(plugin process):控制网站使用的所有插件,见上图插件代理程序
  • GPU 进程(GPU process):处理 GPU 任务,GPU 处理来自多个应用的请求,但是在同个界面上绘制它们
  • 工具进程(utility process):Network & Audio Service 等,与 chrome 服务化有关
  • 扩展进程(extension process): 当前运行的 chrome 扩展程序

浏览器进程:

  • UI 线程: 绘制浏览器的按钮和输入框等
  • 网络线程: 处理网络堆栈,从互联网接收数据
  • 存储线程:控制文件的访问等

约定:由于 chrome 服务化改造,浏览器进程包含的线程在实际运行时可能是以进程的形式呈现,本文不做区分,统一用线程讲述该过程。

Navigation 步骤

(当用户在浏览器地址栏输入一串文本,会发生什么?)

  1. 处理输入

    UI 线程 (地址栏属于UI线程的管辖范围) 解析地址栏中的内容,判断是搜索查询还是 URL;如果是搜索查询则发送到搜索引擎,URL 则到请求的站点。
  2. 开始导航

    用户按下回车键后,UI 线程会发起网络调用获取网站内容。选项卡左上角会显示 loading 图标,网络线程会通过相应的协议(如 DNS 查找和建立 TLS 连接)。如果此时网络线程收到的是重定向,如 301,它会与 UI 线程通信,启动另外一个 URL 请求。
  3. 读取响应

    一旦响应正文开始进入,网络线程将在必要时查看流的前几个字节(Content-Type)。由于 Content-Type 可能缺失或错误,此时会进行 MIME 嗅探
    如果响应是 HTML 文件,则下一步是将数据传递给渲染进程;如果是 zip 或其它文件,意味着是下载请求,则将数据传递给下载管理器。
    此时还会进行 SafeBrowsing & CORB(Cross Origin Read Block) 检查。
  4. 查找渲染进程

    一旦所有检查完成,网络线程告知 UI 线程数据已准备就绪,UI 线程接下来会找到一个渲染进程来渲染网页。
    由于网络请求可能需要耗费数百毫秒,这里进行了优化。在第2步 UI 线程向网络线程发送 URL 请求时,它已经知道正在导航的站点,UI 线程会尝试并行查找或启动渲染进程,如果一切按预期进行,当网络线程接收到数据时,渲染进程已准备就绪。如果导航跨站重定向,备用渲染进程可能不会用到。
  5. 提交导航

    用浏览器进程向渲染进程发送 IPC 以提交导航,它还会传递数据流,这样渲染进程可以继续接收 HTML 数据。一旦浏览器进程收到确认--渲染进程内部已进行提交,则导航完成,文档加载阶段开始。
    此时地址栏已更新,安全指示器和站点设置 UI 都反映新页面的站点信息。选项卡的会话历史将更新,为了方便恢复选项卡/会话,关闭选项卡/窗口时,会话历史记录会存储在磁盘上。
  6. 额外步骤:初始加载完成

    导航提交后,渲染进程继续加载资源并渲染页面,当渲染进程"完成"渲染后,它将 IPC 发送回浏览器进程(页面中所有 frame 的 onload 事件都已触发且执行完成之后),此时 UI 线程停止 loading 图标。完成加引号是因为客户端 JavaScript 此后仍可加载其它资源。

其它情况

导航到其它站点

如果用户再次往地址栏里输入一个不同的 URL,会发生什么?

浏览器进程将通过相同的步骤导航到指定的站点,但在这之前需要检查当前的站点是否注册了 beforeunload 事件。选项卡内的所有内容(当然包括 JavaScript 代码)都是由渲染进程控制的,所以有新的导航请求时,浏览器进程会与当前的渲染进程确认。

如果新的导航是由渲染进程发起的(如用户点击链接或 js 执行 window.location = 'xxx'),渲染进程首先检查 beforeunload 事件处理器,然后经过与前面相同的导航过程,唯一的区别是导航请求是由渲染进程发起的。

如果新的导航请求与当前渲染的页面不属于同个站点,会另行调用一个单独的渲染进程处理新的导航请求,而当前的渲染进程会保留下来处理诸如 unload 之类的事件。

注意:仅在需要时添加 beforeunload 事件处理器,因为只有在 beforeunload 事件处理器执行完成之后,才会开始新的导航。

Service Worker

如果网站注册了 service worker 呢?

service worker 注册时会有自己的作用域,当一个导航请求发生时,网络线程会检查当前的域是否注册了service worker,如果注册了,UI 线程会找到渲染进程来执行 service worker 的代码,service worker 可能从缓存中加载数据,也可能发起网络请求。

如果 service worker 最终确定从网络请求数据,这个过程可能会产生延迟,导航预加载可以加快这个过程,它使 service worker 启动时可以并行加载资源。