文 | Mariko Kosaka
译 | 承香墨影
Chrome 算是程序员的标配了,从全球的市场份额来看,它在全球市场的份额已经超过 60%。
在 Chrome 10 周年之际,官方发布了一个系列文章,用图解的方式,很清晰的讲解了现代浏览器的运行原理。
本文是该系列的第二篇,为了便于阅读,我做了简单修改和注释,建议顺序阅读:
老规矩,觉得本文有帮助,就点赞、留言或者转发分享吧。你喜欢总要让我知道吧!
导航的背后,发生了什么?
这是关于 Chrome 内部原理系列文章中的第 2 篇。在上一篇文章中,我们研究了不同的进程和线程中,如何处理浏览器的不同功能。在这篇文章中,我们深入研究了每个进程和线程如何进行通信以及最终显示网站。
让我们看一个最常见的操作:你在浏览器中输入 URL,然后浏览器从网络获取数据,并显示页面。在这篇文章中,我们将重点讲解用户请求网站,以及浏览器如何呈现网页的部分,这个操作也被称为导航。
从浏览器进程开始
正如我们在第 1 部分中所述:CPU,GPU,内存和多进程架构,选项卡外部的所有内容,都由浏览器进程来处理。
浏览器进程具有诸如用来绘制浏览器按钮和输入文本的 UI 线程;处理网络堆栈以及从互联网接受数据的网络线程;控制对文件访问的存储线程等等。例如,当你在地址栏中输入 URL 时,输入的这个动作,将有浏览器进程的 UI 线程处理
![](https://i-blog.csdnimg.cn/blog_migrate/51ffadff63c7f22e8777dd8e659778be.png)
一次简单的导航
1. 处理输入
当用户开始输入地址栏时,UI 线程首先会去判断 “这是搜索查询还是 URL?”。
在 Chrome 中,地址栏同时具备搜索查询的功能,因此 UI 线程需要解析并确定是将请求发送到搜索引擎,还是发送到待请求的网站。
![](https://i-blog.csdnimg.cn/blog_migrate/7d6e40e233e31ef2d1869dbdbbc03ca1.png)
2. 开始导航
当用户点击 “Enter” 时,UI 线程启动网络请求,以获取站点内容。加载中状态显示在选项卡的左边,并且网络线程通过适当的协议,如 DNS 查找和 TLS 为请求建立连接。
![](https://i-blog.csdnimg.cn/blog_migrate/9675f176fd2057acf8c679fa5e247564.png)
此时,网络线程可以接收并处理 HTTP 301 这样的服务器重定向,在这种情况下,网络线程与处理服务器重定向请求的 UI 线程通信,之后将启动另一个 URL 请求。
3. 读取 Response
一旦响应体(payload)开始接收,网络线程会在必要时,查看数据流的前几个字节,获取响应报文头。
报文头的 Content-Type
用来说明它是什么类型的数据,但由于它可能丢失或错误,所以在这里还需要通过 MIME 类型嗅探,来进一步判断数据类型。这其实是一个很复杂的逻辑,你可以通过阅读源码,以了解不同的浏览器如何处理 Content-Type/Payload。
![](https://i-blog.csdnimg.cn/blog_migrate/4101163d2fcda5db53f13b286000cb86.png)
如果判断是 HTML 文件,那么下一步就是将数据,传递给渲染器进程,但如果它是 zip 文件或其他文件,则表示这是下载请求,因此需要将数据传递给下载管理器。
![](https://i-blog.csdnimg.cn/blog_migrate/56218a36b052e3a872953d1422908d48.png)
网络线程会检查响应数据是否是来自 Safe Browsing(安全站点)的 HTML。如果域或响应数据与已知的恶意网站相匹配,则网络线程会发出警告,并显示警告页面。此外,还可能会触发 CORB(Cross Origin Read block)检查,用来确保敏感的跨站点数据无法进入渲染器进程。
需要注意,CORB 发生在 subDownloads 阶段,但是不会发生在顶级导航中。在顶级导航中,会创建一个安全上下文,而浏览器会决定那个渲染器应该处理它,因此,在这种情况下,CORB 是不会执行的。
4. 查找渲染器进程
完成所有的检查,并且当网络线程确定浏览器会导航到请求的站点时,网络线程将通知 UI 线程,数据已经准备就绪。然后,UI 线程通知渲染器进程,进行网页的渲染。
![](https://i-blog.csdnimg.cn/blog_migrate/1f6ba9bdc7db6e2f429fd053a922bac0.png)
网络请求是一个耗时操作,这中间可能需要几百毫秒的才能得到响应,因此会对此过程进行加速优化。
当 UI 线程在步骤 2 时,向网络线程发送 URL 请求,浏览器已经能确定他们正在导航的站点。UI 线程尝试与网络请求并行执行,主动查找复用或启动渲染器进程。这样如果一切顺利,则当网络线程开始接收数据时,渲染器进程已处于待用状态。如果导航重定向的 URL 跨站点了,则可能不会使用此备用进程,在这种情况下就需要其他进程来处理了。
5. 提交导航
现在数据和渲染器进程已经准备就绪,为了提交导航,IPC 将从浏览器进程发送一个数据流到渲染器进程。因为此处传递的是一个数据流,渲染器进程可以继续从数据流中接收 HTML 数据。一旦浏览器进程监听到渲染器进程中已经确认提交,一次导航就算完成了,接下来就是文档加载阶段。
此时,地址栏会更新,安全锁(HTTPS证书安全)和站点设置 UI 会显示新页面的站点信息。选项卡的历史记录将更新,因此后退/前进按钮将允许操作之前的浏览器历史。同时会将历史记录存储在磁盘上,以确保关闭选项卡或窗口后,依然可以浏览历史以及还原窗口。
![](https://i-blog.csdnimg.cn/blog_migrate/0672ecec1127d4a535901ffb7cc6035f.png)
额外步骤:初始加载完成
提交导航后,渲染器进程继续加载资源,并显示页面。我们将在下一篇文章中详细介绍该阶段的情况。
一旦渲染器进程 “完成” 渲染,它就会通过 IPC 将消息通知回浏览器进程(这是在所有页面中的 onload
事件都触发之后执行的)。此时,UI 线程会隐藏选项卡上的加载进度图标。
这里的 “完成” 之所以加引号,因为客户端 JavaScript 仍然可以加载额外的资源,并在此之后呈现新的视图。
![](https://i-blog.csdnimg.cn/blog_migrate/3b9a4fbf95492e42246dc474f78b1d8a.png)
导航到其他站点
简单的导航,到这里就算完成了。但是如果用户再次将不同的 URL 放到地址栏会发生什么?
浏览器进程会通过相同的步骤,导航到不同的站点。但在此之前,它需要检查当前显示的网站是否注册了 beforeunload
事件。
当你尝试新导航或关闭选项卡时,beforeunload
可以触发显示 “离开这个网站吗?” 这个弹窗,用以提示用户。选项卡内的所有内容,包括 JavaScript 代码都是由渲染器进程处理,因此浏览器进程必须在新导航请求发起时,检查当前的渲染器进程。
注意:不要无条件的添加
beforeunload
,它会产生更多的延迟,应该仅在需要时才监听此事件。例如,警告用户,他们可能会丢失在页面上输入的数据。
![](https://i-blog.csdnimg.cn/blog_migrate/5b1e0fb27868de861cacc11fa0eb1a37.png)
如果导航是从渲染器进程发起的,例如用户点击超链接或 JavaScript 代码执行 window.location="https://newsite.com"
,则渲染器进程首先检查 beforeunload
。然后,它将执行浏览器进程启动导航相同的过程,唯一的区别,是导航请求是从渲染器进程启动到浏览器进程。
当新导航进行到与当前渲染的网站不同的网站时,会调用单独的渲染进程来处理新导航,同时保持当前渲染进程用于处理类似 unload
事件。更多信息,可以参阅页面生命周期概览以及如何使用 Page Lifecycle API 。
![](https://i-blog.csdnimg.cn/blog_migrate/546f1231c83727fdd7c013201faf07b0.png)
在上图中,浏览器进程到渲染器进程,有两次 IPC,用来通知渲染新页面并通知旧渲染器进程 Unload。
Service Worker 的情况下
最近在导航过程中,引入了 Service Worker(服务工作线程)。Service Worker 是浏览器独立于网页运行的服务脚本,它允许 Web 开发人员,更好地控制本地缓存内容,以及必要时从网络获取新数据。如果将 Service Worker 设置为从缓存加载页面,则无需从网络请求数据。
![](https://i-blog.csdnimg.cn/blog_migrate/76b02218523790bf6740065dd32fb344.png)
需要注意,Service Worker 是在渲染器进程中运行 JavaScript 代码,但是当导航请求发起时,浏览器进程如何知道该站点有 Service Worker 呢?
注册 Service Worker 时,将保留 Service Worker 的范围引用。当导航发生时,网络线程根据注册的 Service Worker 范围检查域,如果为该 URL 注册过 Service Worker,则 UI 线程找到渲染器进程,并执行 Service Worker 的逻辑代码。 Service Worker 可以直接从缓存中加载数据,而无需通过网络获取数据,如果在缓存中没有查到数据,再通过网络请求获取数据。
![](https://i-blog.csdnimg.cn/blog_migrate/302317113b9a1c3b8e5e54a1d49d7253.png)
如上图,浏览器进程中的 UI 线程,启动渲染器进程来处理 Service Worker,除此之外,还有可能工作线程会从网络请求数据。
导航预加载
可以预想到,如果 Service Worker 最终需要从网络获取数据,则浏览器进程和渲染器进程之间的通信,可能会导致延迟。导航预加载是一种通过与 Service Worker 并行加载资源,来加速此过程的机制。它用 Header 标记这些请求,允许服务器决定为这些请求发送不同的内容。例如,只更新部分数据而不是完整文件。
![](https://i-blog.csdnimg.cn/blog_migrate/ad673e4840d3783c94609a3ebd250211.png)
小结
在本篇文章中,我们研究了在导航的过程中,执行的流程以及响应头和客户端 JavaScript 等 Web 应用程序的代码,如何与浏览器进行交互。了解了浏览器通过网络获取数据的步骤,可以更容易地理解为什么开发导航预加载等 API。
在下一篇文章中,我们将深入探讨浏览器如何处理 HTML/CSS/JavaScript 并呈现在页面上。
如果本文有帮助,留言、点赞和转发,是最大的支持,谢谢!
原文地址:
https://developers.google.com/web/updates/2018/09/inside-browser-part2
参考阅读:
MIME Type:https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
SafeBorwsing:https://safebrowsing.google.com/
CORB:https://www.chromium.org/Home/chromium-security/corb-for-developers
页面生命周期:https://developers.google.com/web/updates/2018/07/page-lifecycle-api#overview_of_page_lifecycle_states_and_events
Page Lifecycle API:https://developers.google.com/web/updates/2018/07/page-lifecycle-api
Service Worker:https://developers.google.com/web/fundamentals/primers/service-workers/
「联机圆桌」????推荐我的知识星球,一年 50 个优质问题,上桌联机学习。
公众号后台回复成长『成长』,将会得到我准备的学习资料,也能回复『加群』,一起学习进步;你还能回复『提问』,向我发起提问。
推荐阅读:
图解 Chrome,架构篇 | 利用预处理脚本,管理小程序代码| 分词,科普及解决方案| 图解:HTTP 范围请求 | 小程序学习资料 |HTTP 内容编码 | 辅助模式实战 | 辅助模式玩出花样 | 小程序 Flex 布局
![](https://i-blog.csdnimg.cn/blog_migrate/ec2d6339cc5de9b0c4215cc2226f6964.png)
听说喜欢留言和分享的人,会有好运来哦