给前端以福利,给编程以复利。大家好,我是大家的林语冰。
00. 写在前面
在上一篇文章中,我们探讨了不同的进程和线程如何处理浏览器的不同部分。这是探讨 Chrome 内部工作原理的系列博客的第 2 部分,本系列博客一共有四大篇章。 在本文中,我们将深度学习展示网站时,每个进程和线程如何通信。
事实上,这是一道回头率超高的面试题:你在浏览器中输入 URL,然后浏览器从互联网获取数据并显示页面,这个过程发生了什么事?
在这篇文章中,我们来看看谷歌官方博客的标准答案,我们会重点关注用户请求站点和浏览器准备渲染页面的部分,这个过程也称为 导航。
免责声明 本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考。英文原味版请传送 Inside look at modern web browser (part 2)。
01. 导航从浏览器进程开始
我们在上一篇博客中科普了 CPU、GPU、内存和多进程架构,选项卡之外的所有内容都由浏览器进程处理。
浏览器进程分为:
- UI 线程:绘制浏览器的按钮和输入字段
- 网络线程:处理网络堆栈以从互联网接收数据
- 存储线程:控制对文件的访问等
- 其他线程等等
当你在地址栏中输入 URL 时,你的输入将由浏览器进程的 UI 线程处理。
02. 一个简单的导航过程
02-1. 第 1 步:处理输入
当用户在地址栏中输入内容时,UI 线程询问的第一件事是"用户输入的是搜索查询,还是 URL?"。
在 Chrome 浏览器中,地址栏也是一个搜索输入字段,因此 UI 线程需要解析,并决定是将你的输出发送到搜索引擎,还是传送到你请求的站点。
02-2. 第 2 步:开始导航
当用户按下回车键时,UI 线程会启动网络调用来获取站点内容。
加载旋转器会显示在选项卡的一角,网络线程会执行适当的协议,比如 DNS 查找,并为请求建立 TLS 连接。
此时,网络线程可以接收诸如 HTTP 301 之类的服务器重定向标头。
在这种情况下,网络线程与服务器正在请求重定向的 UI 线程进行通信。然后,将发起另一个 URL 请求。
02-3. 第 3 步:读取响应
一旦响应主体(有效负载)开始进入,网络线程就会在必要时查看数据流的前几个字节。
响应的 Content-Type 标头应该说明它是什么类型的数据,但由于它可能丢失或错误,因此 MIME 类型嗅探是在此处完成的。
如果响应是 HTML 文件,那么下一步是将数据传递到渲染器进程;但如果它是 zip 文件或其他文件,则意味着它是下载请求,因此它们需要将数据传递到下载管理器。
这也是安全浏览检查发生的地方。如果域名和响应数据与已知的恶意站点匹配,那么网络线程会发出警报,显示警告页面。
此外,还会进行跨源读取阻止(CORB)检查,确保敏感的跨站点数据不会进入渲染器进程。
02-4. 第四步:找到渲染进程
一旦完成所有检查,且网络线程确信浏览器应该导航到请求站点,网络线程就会告诉 UI 线程数据已经准备妥当。然后 UI 线程会找到渲染器进程进行网页渲染。
由于网络请求可能需要数百毫秒才能得到响应,因此应用了加速此过程的优化。
当 UI 线程在步骤 2 中向网络线程发送 URL 请求时,它已经知道它们要导航到哪个站点。UI 线程会尝试与网络请求并行地主动查找或启动渲染器进程。
这样,如果一切如期进行,那么当网络线程接收到数据时,渲染器进程就已经处于待机位置。如果导航重定向跨站点,那可能不会使用此备用进程,在这种情况下可能需要不同的进程。
02-5. 第 5 步:提交导航
现在数据和渲染器进程已准备就绪,IPC(进程间通信)会从浏览器进程发送到渲染器进程,提交导航。
IPC 还传递数据流,以便渲染器进程可以继续接收 HTML 数据。一旦浏览器进程听到渲染器进程中已发生提交的确认,导航就完成,且文档加载阶段开始。
此时,地址栏已更新,安全指示器和站点设置 UI 反映了新页面的站点信息。该选项卡的会话历史记录将更新,因此后退/前进按钮将逐步浏览刚刚导航到的站点。
为了在关闭选项卡或窗口时方便恢复选项卡/会话,会话历史记录将存储在磁盘上。
02-6. 额外步骤:初始加载完成
一旦提交导航,渲染器进程就会继续加载资源,并渲染页面。我们将在下一篇文章中详细介绍此阶段发生的事情。
一旦渲染器进程"完成"渲染,它就会将 IPC 发送回浏览器进程,这发生在页面中的所有帧上触发所有 onload
事件并完成执行之后。
此时,UI 线程会停止选项卡上的加载旋转器。这样,一个简单的导航过程就完成了!
03. 导航到不同的站点
但是,如果用户再次在地址栏中输入不同的 URL,又会发生什么呢?
浏览器进程将通过相同的步骤导航到不同的站点。但在此之前,它需要检查当前渲染的站点是否涉及 beforeunload
事件。
当你尝试离开或关闭选项卡时,beforeunload
可以创建"离开此站点?"的警告消息。选项卡内包括 JavaScript 代码的所有内容均由渲染器进程处理,因此当新的导航请求传入时,浏览器进程必须检查当前渲染器进程。
粉丝请注意,请勿滥用 beforeunload
处理程序。它会产生更多延迟,因为处理程序需要在导航开始之前执行。当且仅当需要时添加此事件处理程序,举个栗子,如果需要警告用户可能会丢失在页面上输入的数据。
如果导航是从渲染器进程启动的,比如用户单击链接,或者客户端 JavaScript 已运行 window.location = "https://newsite.com"
,那么渲染器进程首先检查 beforeunload
处理程序。
然后,它会经历与浏览器进程启动导航相同的过程。唯一的区别在于,导航请求是从渲染器进程启动到浏览器进程的。
当新导航指向与当前渲染站点不同的站点时,将调用一个单独的渲染进程来处理新导航,同时保留当前渲染进程来处理诸如 unload
之类的事件。
04. Service Worker
导航过程最近的一项变化是引入了 Service Worker。
Service Worker 是一种在应用程序代码中编写网络代理的方法;允许 Web 开发者更好地控制本地缓存的内容,以及何时从网络获取新数据。
如果 Service Worker 设置为从缓存加载页面,则无需从网络请求数据。
粉丝请注意,Service Worker 是在渲染器进程中运行的 JavaScript 代码。但是当导航请求到来时,浏览器进程如何知道该站点有 Service Worker?
注册 Service Worker 后,Service Worker 的作用域将保留作为参考。
发生导航时,网络线程会根据注册的 Service Worker 作用域检查域名,如果为该 URL 注册了 Service Worker,那么 UI 线程会找到渲染器进程,执行 Service Worker 代码。
Service Worker 可以从缓存加载数据,而无需从网络请求数据,或者它可以从网络请求新资源。
05. 导航预加载
如果 Service Worker 最终决定从网络请求数据,那么浏览器进程和渲染器进程之间的往返可能会导致延迟。
导航预加载是一种通过在 Service Worker 启动时并行加载资源加速此过程的机制。它用标头标记这些请求,允许服务器决定为这些请求发送不同的内容;举个栗子,仅更新数据而不是完整文档。
高潮总结
在本文中,我们探讨了导航期间发生的幕后细节,以及响应标头和客户端 JavaScript 等 Web 应用程序代码如何与浏览器交互。
了解浏览器从网络获取数据所经历的步骤,可以更轻松地理解导航预加载等 API 的设计动机。在下一篇博客中,我们将深入探讨浏览器如何执行 HTML/CSS/JavaScript,从而渲染页面。
参考文献
- Blog :developer.chrome.com/blog/inside...
- MDN :developer.mozilla.org/en-US/docs/...
- V8 :github.com/v8/v8
粉丝互动
本期话题是:如何回答:你在浏览器中输入 URL,然后浏览器获取数据并显示页面,这个过程发生了什么事??你可以在本文下方自由言论,文明科普。
欢迎持续关注"前端俱乐部",给前端以福利,给编程以复利。
坚持阅读的小伙伴可以给自己点赞!谢谢大家的点赞,掰掰~