这是一个经典的面试题:
了解这个问题先看下chrome进程架构图
从图中可以看出Chrome浏览器包括:1个浏览器主进程,1个GPU进程,一个网络进程,多个渲染进程,多个插件进程(可以没有)
接下来看下从输入URL到页面展示完成示意图:
1.用户输入回车
当输入的协议或主机名不合法时,浏览器会将地址栏中输入的文字传给默认的搜索引擎,来合成新的带搜索关键字的URL
当如果判断输入内容符合 URL 规则,比如输入的是 www.baidu.com,那么地址栏会根据规则,把这段内容加上协议,合成为完整的 URL www.baidu.com
接着就进入了加载阶段
当浏览器刚开始加载一个地址之后,标签页上的图标便进入了加载状态。但此时图中页面显示的依然是之前的页面内容,因为需要等待提交文档阶段,页面内容才会被替换。
2.URL请求过程
接下来,进入页面资源请求过程。浏览器进程会通过进程间通信(IPC)把 URL 请求发送至网络进程,网络进程接收到 URL 请求后,发起 URL 请求流程
网络进程会查找本地缓存是否缓存了该资源。如果有缓存资源,那么直接返回资源给浏览器进程;如果在缓存中没有查找到资源,那么直接进入网络请求流程。这请求前的第一步是要进行 DNS 解析,获取请求域名的服务器 IP 地址
接下来就是利用 IP 地址和服务器建立 TCP 连接。
连接建立之后,浏览器端会构建请求行、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。下面的图打印出服务器收到的信息:
服务器接收到请求信息后,会根据请求信息生成响应数据(包括响应行、响应头和响应体等信息),并发给网络进程。等网络进程接收了响应行和响应头之后,就开始解析响应头的内容了。
收到服务器返回信息
服务器返回的响应头的状态码是 200,这是告诉浏览器一切正常,可以继续往下处理该请求了。
然后浏览器会根据 Content-Type 的值来决定如何显示响应体的内容。
不同 Content-Type 的后续处理流程也截然不同。如果 Content-Type 字段的值被浏览器判断为下载类型,那么该请求会被提交给浏览器的下载管理器,同时该 URL 请求的导航流程就此结束
3. 准备渲染进程
Chrome 会为每个页面分配一个渲染进程,也就是说,每打开一个新页面就会配套创建一个新的渲染进程(打开同一站点的共用一个渲染进程)
4. 提交文档
“提交文档”的消息是由浏览器进程发出的,渲染进程接收到“提交文档”的消息后,会和网络进程建立传输数据的“管道"
等文档数据传输完成之后,渲染进程会返回“确认提交”的消息给浏览器进程。
浏览器进程在收到“确认提交”的消息后,会更新浏览器界面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 Web 页面。
5.渲染阶段
先理解什么是DOM
从网络传给渲染引擎的 HTML 文件字节流是无法直接被渲染引擎理解的,所以要将其转化为渲染引擎能够理解的内部结构,这个结构就是 DOM。DOM 提供了对 HTML 文档结构化的表述。
- 从页面的视角来看,DOM 是生成页面的基础数据结构。
- 从 JavaScript 脚本视角来看,DOM 提供给 JavaScript 脚本操作的接口,通过这套接口,JavaScript 可以对 DOM 结构进行访问,从而改变文档的结构、样式和内容。
渲染进程接收到“提交文档”的消息后,会和网络进程建立传输数据的“管道"
在渲染引擎内部,有一个叫HTML 解析器(HTMLParser)的模块,它的职责就是负责将 HTML 字节流转换为 DOM 结构。
网络进程接收到响应头之后,会根据响应头中的 content-type 字段来判断文件的类型,比如 content-type 的值是“text/html”,那么浏览器就会判断这是一个 HTML 类型的文件,然后为该请求选择或者创建一个渲染进程。渲染进程准备好之后,网络进程和渲染进程之间会建立一个共享数据的管道 网络进程接收到数据后就往这个管道里面放,而渲染进程则从管道的另外一端不断地读取数据,并同时将读取的数据“喂”给 HTML 解析器。你可以把这个管道想象成一个“水管”,网络进程接收到的字节流像水一样倒进这个“水管”,而“水管”的另外一端是渲染进程的 HTML 解析器,它会动态接收字节流,并将其解析为 DOM。
样式计算(Recalculate Style)
样式计算的目的是为了计算出 DOM 节点中每个元素的具体样式,这个阶段大体可分为三步来完成。
CSS 样式来源主要有三种:
- 通过 link 引用的外部 CSS 文件
- <style>标记内的 CSS
- 元素的 style 属性内嵌的 CSS
和 HTML 文件一样,浏览器也是无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets
为了加深理解,你可以在 Chrome 控制台中查看其结构,只需要在控制台中输入 document.styleSheets,然后就看结构
3. 计算出 DOM 树中每个节点的具体样式
布局阶段
你可能注意到了 DOM 树还含有很多不可见的元素,比如 head 标签,还有使用了 display:none 属性的元素。所以
在显示之前,我们还要额外地构建一棵只包含可见元素布局树
分层
因为页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树
要想直观地理解什么是图层,你可以打开 Chrome 的“开发者工具”,选择“Layers”标签,就可以可视化页面的分层情况,如下图所示:
图层绘制
渲染引擎实现图层的绘制会把一个图层的绘制拆分成很多小的绘制指令然后再把这些指令按照顺序组成一个待绘制列表,提交给合成线程
合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
合成线程发送绘制图块命令DrawQuad给浏览器进程。
浏览器进程根据 DrawQuad 消息生成页面显示到显示器上。
问题:
- 为什么一定要通过浏览器内核请求资源,再将数据转发给渲染进程而不是直接去请求资源
- 为什么渲染进程只负责生成图片,生成图片还有通过IPC通知浏览器内核模块,交由内核去展示图片
基于安全考虑:下载一个恶意程序,不去执行他,恶意程序是不会生效的,解析执行这些内容就要小心了,所以渲染进程和操作系统进行了隔离即是安全沙箱,所以渲染进程需要访问系统资源都是通过浏览器内核实现,通过IPC通信获取结果!