深入了解现代浏览器(第二部分)
作者:互联网
译自:Inside look at modern web browser (part 2)
导航中发生了什么
这是一个由4部分组成的博客系列的第2部分,该系列文章着眼于Chrome的内部运作。在上一篇文章中,我们研究了浏览器的不同部分对应了哪些进程线程。在这篇文章中,我们将深入探讨每个进程和线程如何通信以显示网站。
让我们看一个简单的网页浏览用例:您在浏览器中键入一个URL,然后浏览器从互联网获取数据并显示一个页面。在这篇文章中,我们将重点介绍用户请求站点并且浏览器准备呈现页面的部分 - 也称为导航(navigation)。
导航从浏览器进程开始
正如我们在第 1 部分中介绍的那样:CPU、GPU、内存和多进程体系结构,选项卡之外的所有内容都由浏览器进程处理。浏览器进程具有诸如绘制浏览器按钮和输入字段的UI线程,处理网络堆栈以从Internet接收数据的网络线程,控制文件访问的存储线程等线程。在地址栏中键入 URL 时,输入将由浏览器进程的 UI 线程处理。
(Figure 1: Browser UI at the top, diagram of the browser process with UI, network, and storage thread inside at the bottom)
一个简单的导航
第一步:处理输入
当用户开始在地址栏中输入内容时,UI 线程询问的第一件事是“这是搜索查询还是 URL?”。 在 Chrome 中,地址栏也是一个搜索输入字段,因此 UI 线程需要解析并决定是将您发送到搜索引擎还是您请求的站点。
(Figure 1: UI Thread asking if the input is a search query or a URL)
第二步:开始导航
当用户点击回车时,UI 线程会发起网络调用以获取站点内容。 加载微调器( Loading spinner)显示在选项卡的一角,网络线程通过适当的协议,如 DNS 、TLS等,为请求查找和建立连接。
(Figure 2: the UI thread talking to the network thread to navigate to mysite.com)
此时,网络线程可能会收到类似于 HTTP 301 的服务器重定向标头。在这种情况下,对于UI 线程,服务器对它返回了“需要重定向”的说明(requesting redirect),UI线程继续和网络线程进行通信。然后,将启动另一个 URL 请求。
原文:At this point, the network thread may receive a server redirect header like HTTP 301. In that case, the network thread communicates with UI thread that the server is requesting redirect. Then, another URL request will be initiated.
第三步:读取响应
一旦响应主体(有效负载)开始进入,网络线程会在必要时查看流的前几个字节。 响应的 Content-Type 标头应该说明它是什么类型的数据,但由于它可能丢失或错误,因此在这里进行 MIME 类型嗅探。 如源代码中所述,这是一项“棘手的业务”。 您可以阅读相应注解以了解不同的浏览器如何处理内容类型/有效负载对。
(Figure 3: response header which contains Content-Type and payload which is the actual data)
如果响应是一个 HTML 文件,那么下一步就是将数据传递给渲染器进程,但如果它是一个 zip 文件或其他文件,那么这意味着它是一个下载请求,所以他们需要将数据传递给 下载管理器。
(Figure 4: Network thread asking if response data is HTML from a safe site)
这也是进行安全浏览检查的地方。 如果域和响应数据似乎与已知的恶意站点匹配,则网络线程会发出警报以显示警告页面。 此外,为了确保敏感的跨站点数据不会进入渲染器进程,还会进行跨源读取阻止 (CORB,Cross Origin Read Blocking) 检查。
第 4 步:查找渲染器进程
一旦完成所有检查并且网络线程确信浏览器应该导航到请求的站点,网络线程就会告诉 UI 线程数据已准备好。 UI线程然后找到一个渲染器进程来进行网页的渲染。
(Figure 5: Network thread telling UI thread to find Renderer Process)
由于网络请求可能需要数百毫秒才能得到响应,因此应用了加速此过程的优化。 当 UI 线程在第 2 步向网络线程发送 URL 请求时,它已经知道他们正在导航到哪个站点。 UI 线程尝试主动查找或启动与网络请求并行的渲染器进程。 这样,如果一切按预期进行,当网络线程接收到数据时,渲染器进程已经处于待机位置。 如果导航重定向跨站点,则可能不会使用此备用进程,在这种情况下,可能需要不同的进程。
第 5 步:提交导航
现在数据和渲染器进程已经准备好,一个 IPC 从浏览器进程发送到渲染器进程以提交导航。 这个IPC还传递数据流,因此渲染器进程可以持续接收 HTML 数据。 一旦浏览器进程监听到在渲染器进程中确认commit(数据传输完成的确认),导航就完成了,文档加载阶段就开始了。
此时,地址栏更新,安全指示器和站点设置 UI 反映了新页面的站点信息。 该选项卡的会话历史记录将被更新,因此后退/前进按钮将逐步浏览刚刚导航到的站点。 为了在您关闭选项卡或窗口时促进选项卡/会话恢复,会话历史记录存储在磁盘上。
(Figure 6: IPC between the browser and the renderer processes, requesting to render the page)
额外步骤:初始加载完成
提交导航后,渲染器进程会继续加载资源并渲染页面。 我们将在下一篇文章中详细介绍此阶段发生的事情。 一旦渲染器进程“完成”渲染,它会将 IPC 发送回浏览器进程(这是在页面中的所有帧上触发所有 onl oad 事件并完成执行之后)。 此时,UI 线程停止选项卡上的加载微调器。
我说“完成”,因为在此之后客户端 JavaScript 仍然可以加载其他资源并呈现新视图。
(Figure 7: IPC from the renderer to the browser process to notify the page has "loaded")
导航到其他站点
简单的导航就完成了! 但是如果用户再次将不同的 URL 放到地址栏会发生什么? 好吧,浏览器进程通过相同的步骤导航到不同的站点。 但在此之前,它需要检查当前呈现的站点是否关心 beforeunload 事件。
beforeunload 可以创建“离开此站点?” 当您尝试离开或关闭选项卡时发出警报。 选项卡内的所有内容,包括您的 JavaScript 代码,都由渲染器进程处理,因此当新的导航请求进来时,浏览器进程必须检查当前的渲染器进程。
警告
不要添加无条件的 beforeunload 处理程序。 它会产生更多的延迟,因为处理程序需要在导航开始之前执行。 仅在需要时才应添加此事件处理程序,例如,如果需要警告用户他们可能会丢失他们在页面上输入的数据。
(Figure 8: IPC from the browser process to a renderer process telling it that it's about to navigate to a different site)
如果导航是从渲染器进程启动的(例如用户单击链接或客户端 JavaScript 已运行 window.location = "https://newsite.com"),则渲染器进程首先检查 beforeunload 处理程序。 然后,它经历与浏览器进程启动导航相同的过程。 唯一的区别是导航请求是从渲染器进程启动到浏览器进程的。
当新导航指向与当前渲染的站点不同的站点时,会调用一个单独的渲染进程来处理新的导航,而当前的渲染进程会被保留以处理诸如卸载之类的事件。 有关更多信息,请参阅页面生命周期状态概述以及如何使用页面生命周期 API 挂钩事件。
如果是 Service Worker
这个导航过程最近的一个变化是引入了服务工作者。 Service Worker 是一种在应用程序代码中编写网络代理的方法; 允许 Web 开发人员更好地控制本地缓存的内容以及何时从网络获取新数据。 如果 Service Worker 设置为从缓存中加载页面,则无需向网络请求数据。
要记住的重要部分是服务工作者是在渲染器进程中运行的 JavaScript 代码。 但是当导航请求进来时,浏览器进程如何知道站点有服务工作者?
(Figure 10: the network thread in the browser process looking up service worker scope)
注册 Service Worker 时,Service Worker 的资源范围作为引用保留(您可以在这篇 The Service Worker 生命周期文章中阅读有关范围的更多信息)。 当导航发生时,网络线程会根据注册的服务工作者范围检查域,如果为该 URL 注册了服务工作者,UI 线程会找到一个渲染器进程以执行服务工作者代码。 Service Worker 可以从缓存中加载数据,从而无需从网络请求数据,或者它可以从网络请求新资源。
(Figure 11: 浏览器进程中的 UI 线程启动渲染器进程以处理服务工作者; 渲染器进程中的工作线程然后从网络请求数据)
导航预加载
如果服务工作者最终决定从网络请求数据,您可以看到浏览器进程和渲染器进程之间的这种往返可能会导致延迟。 Navigation Preload 是一种通过在服务工作者启动时并行加载资源来加速此过程的机制。 它用标头标记这些请求,允许服务器决定为这些请求发送不同的内容; 例如,仅更新数据而不是完整文档。
(图 12:浏览器进程中的 UI 线程启动渲染器进程以处理服务工作者,同时并行启动网络请求)
总结
在这篇文章中,我们研究了导航期间发生的情况以及您的 Web 应用程序代码(例如响应标头和客户端 JavaScript)如何与浏览器交互。 了解浏览器从网络获取数据所经历的步骤可以更容易地理解为什么要开发像导航预加载这样的 API。 在下一篇文章中,我们将深入探讨浏览器如何评估我们的 HTML/CSS/JavaScript 以呈现页面。
你喜欢这篇文章吗? 如果您对未来的帖子有任何问题或建议,我很乐意在下面的评论部分或 Twitter 上的@kosamari 收到您的来信。
Next: 渲染器进程的内部工作
标签:浏览器,渲染器,第二,深入,UI,进程,线程,导航 来源: https://www.cnblogs.com/lhjc/p/16441066.html