「导航渲染流程」你真的知道从输入 URL 到页面展示发生了什么吗?(内附思维导图)
导航渲染流程
从输入到页面渲染这个过程其实可以说得非常复杂,包括了两大步骤,一个是导航流程另一个是渲染流程;
导航流程
1.用户输入 URL
不考虑用户输入搜索关键字的情况, 如果用户输入的内容符合 URL 规则,浏览器就会根据 URL 协议,在这段内容上加上协议合成合法的 URL
2.loading 状态
用户输入完内容,按下回车键,浏览器导航栏显示loading
状态,但是页面还是呈现前一个页面,这是因为新页面的响应数据还没有获取到。
3.浏览器请求
浏览器进程浏览器构建请求行信息,会通过进程间通信(IPC)将 URL 请求发送给网络进程
GET /index.html HTTP1.1
4.网络进程处理
网络进程接收到url
请求后检查本地缓存是否缓存了该请求资源:
- 如果有缓存文件
- 拦截请求,直接 200 返回
- 无缓存文件
- 进入网络请求过程
请求 DNS(返回对应 IP 端口)
- 缓存过当前域名信息
- 直接返回缓存信息
- 未缓存
- 发起请求获取根据域名解析出来的 IP 和端口号
5.TCP 三次握手建立连接
Chrome 有个机制,同一个域名同时最多只能建立 6 个 TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。如果当前请求数量少于 6 个,会直接建立 TCP 连接。
TCP 三次握手建立连接,http 请求加上 TCP 头部——包括源端口号、目的程序端口号和用于校验数据完整性的序号,向下传输 http 请求加上 TCP 头部向下传输
参考链接 三次握手建立连接详细过程
6.数据传输过程
网络层、传输层 在数据包上加上 IP 头部,继续向下传输到底层,底层通过物理网络传输给目的服务器主机
- 网络层在数据包上加上 IP 头部——包括源 IP 地址和目的 IP 地址,继续向下传输到底层
目的服务器主机
- 网络层,解析出 IP 头部,识别出数据部分
- 传输层获取到数据包,解析出 TCP 头部,识别端口
应用层 HTTP 解析请求头和请求体 应用层 HTTP 解析请求头和请求体,如果需要重定向,HTTP 直接返回 HTTP 响应数据的状态code301
或者302
,同时在请求头的Location
字段中附上重定向地址,浏览器会根据code
和Location
进行重定向操作;
如果不是重定向,首先服务器会根据 请求头中的 If-None-Match 的值来判断请求的资源是否被更新,如果没有更新,就返回 304 状态码,相当于告诉浏览器之前的缓存还可以使用,就不返回新数据了;
否则,返回新数据,200 的状态码,并且如果想要浏览器缓存数据的话,就在相应头中加入字段:Cache-Control:Max-age=2000
- 重定向 HTTP 直接返回 HTTP 响应数据的状态
code301
或者302
同时在请求头的 Location 字段中附上重定向地址 - 不是重定向
If-None-Match
,没有更新,就返回304
状态码;否则,返回新数据,200
的状态码Cache-Control
,想要浏览器缓存数据
响应数据又顺着应用层——传输层——网络层——网络层——传输层——应用层的顺序返回到网络进程
7.传输完成,TCP 四次挥手
8.网络进程(数据包解析)
Content-type
网络进程将获取到的数据包进行解析,根据响应头中的Content-type
来判断响应数据的类型
如果是字节流类型,就将该请求交给下载管理器,该导航流程结束,不再进行
如果是 text/html 类型,就通知浏览器进程获取到文档准备渲染
9.渲染进程(渲染进程的主进程)
浏览器会发出“提交文档”的消息给渲染进程,渲染进程收到消息后,会和网络进程建立传输数据的“管道”,文档数据传输完成后,渲染进程会返回“确认提交”的消息给浏览器进程
- 网络进程建立传输数据的“管道”
- 确认提交给浏览器进程
10.浏览器(更新页面状态)
浏览器收到“确认提交”的消息后,会更新浏览器的页面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 web 页面,此时的 web 页面是空白页
此时的 web 页面是空白页,以下列举了三种状态更新
- 安全状态
- 地址栏的 URL
- 前进后退的历史状态
渲染流程(可以看成步骤 9 的补充)
导航被提交后又会怎么样呢?就进入了渲染阶段。
主线程
1.DOM 树
HTML 通过 HTML 解析器解析输出 DOM 树。 下面的 HTML 代码会被解析成上面这种浏览器可以理解的 DOM 树结构:
2.Style 样式计算
把 CSS 转换为浏览器能够理解的结构--styleSheets。 这里一 CSDN 为例可以看到它最新的 styleSheets,会包含引用的外部 CSS 文件、</style>标记内的 CSS、以及内嵌的 CSS;
标准化样式表属性值 将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。
1em
被解析成1px
,red
被解析成了rgb(255,0,0)
等等。计算 DOM 树每个节点的具体样式 最终的样式可以通过控制台 element 的 Computed 查看,关于是怎么计算,涉及继承规则和层叠规则,这里就不细讲了。
3.Layout 布局树
- 创建布局树:遍历 DOM 树中的所有可见节点,加到布局树中。对
display:none
的就忽略不加。 - 布局计算:计算布局树节点的坐标位置。
4.layer 图层绘制列表
浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面。通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。
进入 Layer 页面,操作按钮从左至右功能依次为:平移、旋转、复位。见图:
5.Pain 图层绘制
可以想象你画画,先画远处再画近处,图层绘制也是这种原理。图层绘制阶段,输出待绘制列表。
合成线程
6.栅格化(tiles 图块、raster 光栅化)
在有些情况下,有的图层很大,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下,要绘制所有图层内容的话,就会产生太大的开销,而且也没有必要。
也是因为这个原因,合成线程会将图层进行分块划分为图块然后再栅格化,将图块转换为位图,而渲染进程通常不做或者做不来格栅化,需要跨进程,使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。
7.Display 合成和显示
图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”提交给浏览器进程。
浏览器进程接收 DrawQuad 命令,根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
至此页面就被渲染出来了~~完结撒花 ❀❀❀
渲染流程总结
渲染页面主要做的事:
- 1.将浏览器无法直接理解和使用的 HTML,转换为浏览器能够理解的结构--DOM 树。
- 2.把 CSS 转换为浏览器能够理解的结构--styleSheets,并转换样式表中的属性值,使其标准化,计算出 DOM 树中每个节点的具体样式(根据继承规则和层叠规则)。
- 3.确定 DOM 元素的几何位置信息--布局树,遍历 DOM 树中的所有可见节点,加入到布局树(display:none 不包含),并计算布局树节点的坐标位置。
- 4.如果页面有复杂的效果,如常见的页面滚动,或者使用 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)。
- 5.图层绘制,把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表(联想自己画画)。
- 6.tiles:将图层转换成图块。
- 7.光栅化:通过进程实现图块转换成位图。
- 8.display:浏览器进程拿到 DrawQuad 信息生成页面显示。
DOM 和 HTML 内容几乎是一样的,但是和 HTML 不同的是,DOM 是保存在内存中树状结构,可以通过 JavaScript 来查询或修改其内容。
贴心的我也为你们做了一张图: