首页 > Note > 深入理解现代浏览器 2: Navigation 过程分析

深入理解现代浏览器 2: Navigation 过程分析

2022年6月22日 发表评论 阅读评论

译注:本文翻译自 ChromeDeveloper 博客, 原文发表于2018年底, 部分特性可能与目前 Chrome 有所不同。建议有条件的读者直接阅读原文

本文是深入理解现代浏览器4章中的第2章。此前我们研究了不同进程和线程是如何处理浏览器的不同部件的, 本章中, 我们来深挖每个进程和线程间是如何通信以显示网站的。

我们来看一个简单浏览器网站的用例:在浏览器中输入URL, 然后浏览器从网络上获取数据并显示页面。本章中, 我们聚焦于用户请求站点并准备呈现页面这一部分--即我们说的导航(Navigation)。

从浏览器进程开始说起

browser 进程

browser 进程

如我们之前所说,标签以外的所有内容都由 browser 进程处理。browser 进程拥有很多线程, 像绘制按钮和输入域的 UI 线程,和网络栈交换信息以从网上接收数据的 network 线程,控制文件访问的 storage 线程等等。当你在地址栏输入URL时, browser 进程的 UI线程会处理你的输入。

简单的导航

Step 1: 处理输入

当用户开始在地址栏中输入时,UI线程首先会问"这是一搜索查询还是URL"。 在Chrome 中, 地址栏也是搜索输入域,所以UI线程需要解析并确认是使用搜索引擎还是请求站点。

UI 线程询问输入的是搜索还是 URL

Step 2: 开始导航

当用户点击回车,UI 线程启动一个网络调用并获取站点内容。标签的左上角会显示加载动画,并且 network 线程这通过适当的协议来处理请求, 如 DNS查找与TSL连接建立。

UI 线程告诉 network线程, 要导航到 mysite.com

此时, network 线程可能收到像HTTP301 这样的服务重定向头。这种情况下, network 线程和UI线程交流, 服务请求重定向, 之后开始另一个URL请求。

Step 3: 读取 Response

带有Content-Type的header 以及实际数据的 payload

一旦 response body(payload)流开始进入,如有必要, network 线程查找该流的前几个字节。 response body 的 Content-Type 头会指示数据的类型, 但因为该它可能丢失或是错误的, MIME 类型嗅探 会在此处完成。正如源码中所说, 这是一项“棘手的业务”。你可以阅读代码注释, 了解不同的浏览器是如何处理 content-type/payload 的。

如果 response 是一个 HTML 文件 , 那么下一步就是将数据传递给给渲染进程 。但它若是一个 zip 或其它什么文件, 就意味着这是个下载请求,那么这些它们会将数据传递给下载管理器。

安全浏览检查”也发生在此处。如果域名或response 数据看带来和已知的恶意网站相匹配, 那么network线程会提醒并显示一个警告页面。此外,这里还会进行 跨站点读取阻塞(Cross Origin Read Blocking (CORB) 检查, 来确保敏感的跨站点数据不会进行渲染进程。

Network 线程访问 是否 response 数据是来自安全站点的 HTML

Step 4: 查找渲染进程

当所有的检查都完成, 且 network 线程确信浏览器应该导航到请求的站点之后 ,network 线程告诉 UI线程数据准备好了,然后UI 线程查找并获取一个渲染进程来渲染网页。

UI 线程告诉Network线程去找一个渲染进程

由于网络请求可能耗时数百毫秒获取 response,这里会进行优化以加快此过程。当 Step 2, UI线程发送URL请求到 network 线程时,就已经知道会导航到哪个站点了, UI线程会并行地主动查找, 或启动一个渲染进程。这样如果不出意外, 当network 线程收到数据时, 一个渲染进程已经处理待机位置了。如果导航重跨站点重定向, 这个待机进程可能不会被使用, 这种情况会需要另一个不同的进程。

Step 5: 提交导航

至此, 数据和渲染进程都准备妥当, 提交导航的 IPC 也从 browser 进程发送到 渲染进程。同时还通过流传递了数据,所以渲染进程仍可以继续接收 HTML数据。一旦browser 进程听到渲染进程中确认产生了提交, 导航就完成了,同时开始了文档加载阶段。

此时, 地址栏被更新, 安全标识和站点设置UI 会指示这个新页面的站点信息。标签的历史记录被更新,以便 前进/后退 按钮可以逐步浏览刚刚导航的站点。当关闭标签或窗口时,会话历史会存盘到硬盘上以便还原标签。

browser 进程 和 render进程间的IPC, 请求渲染页面

附加 Step: 初始化加载完成

一旦提交了导航, 渲染进程开始加载资源并渲染页面。下一章我们将详细了解这一步里发生了什么 。当渲染进程完成("finishes")渲染,它将向 browser 进程发回IPC(在页面中所的有frame 的 onload 事件触发并执行完成以后)。此时UI线程会停止标签上的加载动画。

这里说 "finishes" (而不是 "finished" ), 是因为在这之后,客户端的 Javascript 可能仍在加载附加资源,并渲染新的view。

渲染进程到browser 进程的IPC, 提醒页面已加载

导航到不同的站点

我们完成了一个简单的导航。但如果用户在地址栏输入了不同的URL, 会发生什么 呢?browser 进程会通过和之前一样的步骤导航到不同的站点, 但在此之前, 它需要检查当前渲染的站点,是否需要关心 beforeunload 事件。

当你试图导航离开或关闭标签时,beforeunload 会创建一个 “要离开本站吗”的提醒。标签内所有的内容, 包括 JavaScript代码, 都由渲染进程处理, 所以当一个新的导航请求进入时,browser 进程会检查当前的渲染进程。

警告 : 不要添加无条件的 beforeunload 处理器,因为这些处理器会在导航开始之前执行,从而导致更多的延迟。事件处理器应只在需要时添加,比如需要提醒用户进入新页面前可能会丢失数据。

browser 进程到渲染进程的IPC, 告之将导航到一个新的站点

如果导航是从渲染进程启动的(比如用户点击了一个链接,或客户端JavaScript运行了 window.location = "https://newsite.com" ),渲染进程会先检查 beforeunload 处理器,然后它将经历与 browser 进程启动导航相同的过程, 唯一的区别是导航请求是从渲染进程到browser 进程。

当新导航的站点与当前渲染进程里的站点不同时,将调用一个单独的渲染进程来处理它, 同时会保留当前的渲染进程以处理像 unload 之类的事件。参见 页面生命周期概览,以及如何使用 页面生命周期API hook .

从browser进程到新渲染进程的IPC,告之渲染页面;到旧渲染进程的IPC, 告之unload

Service Worker

此导航过程的最新更新是引入了 Service Worker. Service Worker 是一种有你的应用程序代码中编写网络代理的方法, 它允许web开发者更好的控制本地缓存以及何时从网络获取数据。如果设置 ServiceWorker从缓存中加载页面,那么就不必从网络请求数据。

谨记,ServiceWorker 是运行在渲染进程中的JavaScript代码。但导航请求进入时,browser 进程如何知道此站点是否有 ServiceWorker呢?

browser进程中的network线程查找service worker

在注册ServiceWorker时, 将保留ServiceWorker的作用范围的引用(欲知更多关于范围的信息,请参考 Service Worker 生命周期 )。当导航发生时, network 进程会针对ServiceWorker的工作范围检查域名。若为此URL注册了 Service Worker , 那么 UI 线程会查找一个渲染进程以执行 Service Worker 代码。ServiceWorker 可以从缓存中加载数据从而取消从网络请求数据的需求, 亦可从网络请求新资源。

browser进程中的UI线程启动一个渲染进程来处理service worker,然后渲染进程中的worker 进程向网络请求数据

导航预加载

如果Service Worker最终决定从网络请求数据,browser 进程得渲染进程之间的这种往返可能会导致延迟。导航预加载 是一种是在 Service Worker 启动时并行 加载资源, 从而来加速此过程的一种机制。它用一个 header 来标记请求,允许服务器来决定为这些请求发送不同的内容, 比如, 只更新数据而不是整个文档。

browser进程中的 UI 线程启动一个渲染进程处理service worker, 且并行地启动网络请求

小节

本章中, 我们研究了导航中都发生了什么 , 以及你的 web 应用程序代码, 如 response header 和客户端 JavaScript 如何与浏览器交互。了解了浏览器如何从网络获取数据,就更容易理解为何会开始导航预加载 API. 下一章, 我们将深入研究浏览器如何计算(evaluates)我们的 HTML/CSS/JavaScript 来渲染页面。

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.