输入URL是从浏览器进程开始的
- 作者在这里指出,
“任何在tab外的事情都由浏览器进程处理,浏览器process中包含了UI,网络,存储等threads,当你在地址栏中输入URL时,其实是由浏览器process中的UI thread在进行处理”。可能我们会觉得似乎说法上不够准确,的确,我的理解是,作者这里强调的是情况是普通浏览器宿主的条件下,各项服务 process都以thread的形式存在在浏览器process。至于GPU和插件进程,我觉得这可能不是作者想要强调讨论的重点,毕竟这两种进程在这时也并不参与。 - 综上作者这段表述不够精准但是瑕不掩瑜
导航流程
1. 在地址栏处进行输入
Chrome的地址栏有两个功能,地址栏和搜索框,因此其会根据用户输入进行字符串匹配从而判断输入是一个URL还是一个普通的查询,如果是URL直接导航至该地址,否则就导航至默认搜索引擎页。
2. 开始导航
- 当用户输入完成键下回车,可以观察到的左上角的刷新键开始变为"X"
- 在背后,浏览器process中的网络thread开始了诸如DNS查询和TLS连接建立等(这儿其实展开就是网络连接的问题)
- 如果从服务端接受到的返回状态码是301,那么针对新地址的导航将重新开始
3. 读取服务端响应
- 响应头中会有
content-type字段来说明响应内容的类型,但是如果这个字段缺失或者错误,那么MIME嗅探就会发挥作用 - 如果文件是html的时候,文件将被交给渲染进程,导航继续;如果是其他需要被下载的类型比如zip等,那么文件将会被交给下载管理器去处理,导航就此结束
- 当然,如果访问的地址或者响应数据是可疑的,那么浏览器会给出响应的提示
4. 渲染进程参与
- 上述所有的检查都通过了之后(以上涉及到的检查均来自于网络线程),那么UI线程会收到来自网络线程的确认,然后UI线程就会去为这个网页分配一个渲染进程来完成渲染
- 这里Chrome又进行了一步优化,因为网络请求需要的时间不一定,如果UI thread等到网络线程的确认再开始为其分配渲染process会耽误时间,所以在在第二步当UI thread已经知道要导航去的网站的时候就开始准备渲染process了,这样当有数据开始流入时,就可以马上开始
- 但是上述的在等待过程中准备的渲染process不一定会被跨站的导航重定向使用,需要创建一个新的渲染进程
5. 提交导航
- 当数据渲染process都准备就绪,一个IPC会从浏览器process给到渲染process,(一旦渲染process确认(confirm)了这个消息,导航就被结束/提交),于此同时,浏览器process中的网络thread持续接收到的数据流会流向渲染process
- 当渲染process确认导航提交后(
这里其实原文没有说的很明确具体是什么时候,渲染process会confirm,可能作者觉得这些属于特别细枝末节的东西,没必要过分关注,但是为了给读者清晰的呈现,我还是想说一下我自己的理解,这个confirm应该发生的很快,这个目的应该只是渲染process告诉浏览器process,现在就绪了,可以开始下载文档,加载资源了,所以应该不是在文档下载结束之后才confirm,而是马上就confirm),导航就已经结束,导航结束时浏览器上会有一系列的变化,包括地址栏的更新,安全提示符,新页站点信息的UI,session的历史会被更新,从而可以使用前进/后退的按钮;接下来的步骤是加载文档。
6. 初始加载完成
- 导航提交完成,渲染进程会持续接受网络thread流向它的各种网络资源(html,javascript,css,图片等等资源需要加载),以及更重要的渲染工作(会是下节内容的主体,这里不赘述)。
- 一旦渲染process完成了渲染的工作,
即在本页面上的所有frame的onload监听回调函数都被执行完成时,它会回发送一个IPC给浏览器进程,通知浏览器process:"页面加载完成",浏览器process便会停止第一步提到的浏览器左上角的"X",页面展示完全,左上角重新变回刷新键从而标志着初始加载的完成
导航去不同的站点
- 有一个被上述过程忽视的内容,就是当输入的URL是在一个已经加载完毕的页面时,如果当前已经加载的页面上有
beforeunload监听时,需要执行其回调,相关的例子比如在某些网页会提示用户关闭当前tab会丢失某些数据,❗️但是不要滥用这个监听因为可能导致页面的更高延迟❗️ - 如果用户不是在一个已经加载完毕的页面时的地址栏修改URL,而是通过点击超链接,或者javascript执行(或控制台直接执行)的例如
window.location = "https://newsite.com这种直接修改当前页面的位置行为,会有什么不同么?答案是,看起来没什么不同,只不过后者的导航初始化不是通过浏览器进程发起的,而是由当前渲染进程发起。当然新的页面也需要一个新的渲染进程,而不是直接复用当前渲染进程。关于更多页面生命周期的方法,请移步 [page-lifecycle-api | from developers.google.com ]
如果有service worker呢
- service worker 是一个可以让开发人员控制如何获取网络资源的方式,比如在service worker中可以让导航从本地缓存中获取而不是发送网络请求,从而加快了导航过程。
- 有关service worker的更多内容可以去这里[Service Workers: an Introduction | from developers.google.com]
导航预加载
- 在service worker启动的同时加载对应的资源来加快整个导航过程。它会带有一个请求头从而让服务器可以根据不同头部发送不同的内容