浏览器从输入url到渲染页面,发生了什么?
当用户在地址栏输入一个查询关键字时,地址栏会判断输入的关键字是搜索内容还是请求的 URL。
-
如果是搜索内容,地址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URL。
-
如果输入的内容符合 URL 规则,那么地址栏会根据规则,把这段输入的内容加上协议,组成完成的 URL。
当用户按下回车后,这意味着当前页面即将要被替换为新的页面。可以通过监听 beforeunload 事件,在页面退出之前执行一些数据清理的操作,还可以询问用户是否要离开当前页面(例如当前页面有未提交的表单)。
网络请求
接下来,便进入了页面资源请求过程。浏览器进程通过进程间通信(IPC)把 URL 请求发送给网络进程。
1. 查找强缓存
网络进程先检查强缓存,如果命中直接使用,否则进入下一步。
2. DNS 解析
由于输入的是域名,需要进行 DNS 解析,以获取请求域名的服务器 IP 地址。
3. 建立 TCP 连接
Chrome 在同一个域名下要求同时最多只能有 6 个TCP 连接,超过 6 个的话剩下的请求就只能排队等待。
1)建立浏览器与服务器的连接(三次握手)
2)进行数据传输
3)断开连接(四次挥手)
4. 发送 HTTP 请求
网络进程构建请求行、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送请求。
网络响应
服务器接收到请求信息后,会根据请求信息生成响应数据(响应行 + 响应头 + 响应体)发送给浏览器的网络进程。
-
重定向
在接到响应头后,网络进程开始解析响应头,如果发现状态码是 301 或 302,那么说明服务器需要浏览器重定向到其他 URL。这时,网络进程会从响应头中的 Location 字段读取重定向的地址,再发起新的 HTTP 请求,一切又重头开始了。
2.响应数据类型处理
如果状态码为 200,表示一切正常,浏览器可以继续处理该请求。
Content-Type 是 HTTP 头中一个非常重要的字段,它告诉浏览器服务器返回的响应体数据是什么类型,然后浏览器会根据 Content-Type 的值来决定如何显示响应体的内容。
content-type: application/octet-stream
上面表示数据是字节流类型的,浏览器会判断为下载类型,那么该请求会被提交给浏览器的下载管理器,同时该 URL 请求的导航流程到此结束。
content-type: text/html
上面表示返回的是 HTML 格式的内容,浏览器会继续进行导航流程。
浏览器的渲染
5. 准备渲染进程
-
默认情况下,Chrome 会为每个标签页分配一个渲染进程。
-
但是如果从一个页面A打开了另一个新页面B,而新页面B和当前页面A属于同一站点的话,那么新页面就会复用父页面的渲染进程。否则,浏览器进程会为 B 创建一个新的渲染进程。
根域名(例如 baidu.com)和协议(https:// 或 http://)相同,就属于同一站点。
6. 提交文档
所谓提交文档,就是指浏览器进程将网络进程接收到的 HTML 数据提交给渲染进程。
-
当浏览器进程接收到网络进程的响应头数据之后,便向渲染进程发起“提交文档”的消息
-
渲染进程收到“提交文档”的消息后,会和网络进程建立传输数据的“管道”
-
等文档数据传输完成之后,渲染进程会返回“确认提交”的消息给浏览器进程
-
浏览器进程在收到“确认提交”的消息后,会更新浏览器界面状态,包括安全状态、地址栏的 URL、前进后退的历史状态等,并更新 Web 页面。
7. 构建 DOM 树
浏览器无法直接理解和使用 HTML,所以需要将 HTML 转化为浏览器能够理解的结构——DOM 树。
8 . 样式计算
-
把 CSS 转换为浏览器能够理解的 styleSheets
CSS 样式来源主要有
-
通过 link 引用的 CSS文件
-
<style> 标记内的CSS
-
元素的 style 属性内嵌的 CSS
-
-
转换样式表中的属性值,使其标准化
例如,2em,blue,bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的,标准化的计算值。这个过程就是属性值标准化。
-
计算出 DOM 树中每个节点的具体样式
这涉及到 CSS 的继承规则和层叠规则。
9 . 布局
-
创建布局树
-
遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中
-
而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,以及属性包含 display: none 的元素。
-
-
布局计算
10 . 分层
由于页面中有很多复杂的效果,如一些复杂的 3D 变换,页面滚动,或使用 z-index 做 z 轴排序等,为了更加方便的实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一颗对应的图层树(LayerTree)。
通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属与他们父节点图层。
通常满足下列两点中任意一点的元素就可以被提升为单独的一个图层:
拥有层叠上下文元素的属性(明确定位属性的元素、定义透明属性的元素、使用 CSS 滤镜的元素等)会被提升为单独的一个图层
需要剪裁的地方也会被创建为图层。如,把 div 的大小限制为 200 * 200 像素,而 div 里面的文字内容比较多,文字所显示的区域肯定会超出 200*200 的面积,这就产生了裁减,渲染引擎会为文字部分单独创建一个层;如果出现滚动条,滚动条也会被提升为单独的层。
11 . 绘制
渲染引擎会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表。
12 . 分块
屏幕上页面的可见区域就叫视口。
在有些情况下,有的页面需要用滚动条滚动很久才可以滚到底部,但是通过视口,用户只能看见页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销。
基于这个原因,合成线程会将图层划分为图块。
13 . 栅格化
然后合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。
通常,栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。
14 . 合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令,然后将该命令提交给浏览器进程。
浏览器进程中的 viz 组件,收到该命令后,将页面内容绘制到内存中,最后再将内存显示在屏幕上。