内容简介:从输入目前
一文摸透从输入 URL
到页面渲染的过程
从输入 URL
到页面渲染需要 Chrome
浏览器的多个进程配合,所以我们先来谈谈现阶段 Chrome
浏览器的多进程架构。
一、 Chrome
架构
目前 Chrome
采用的是多进程的架构模式,可分为主要的五类进程,分别是:浏览器( Browser
)主进程、 GPU
进程、网络( NetWork
)进程、多个渲染进程和多个插件进程;
- 浏览器进程 。主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
- 渲染进程 。核心任务是将
HTML
、CSS
和JavaScript
转换为用户可以与之交互的网页,排版引擎Blink
和JavaScript
引擎V8
都是运行在该进程中,默认情况下,Chrome
会为每个Tab
标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。 -
GPU
进程 。其实,Chrome
刚开始发布的时候是没有GPU
进程的。而GPU
的使用初衷是为了实现3D CSS
的效果,只是随后网页、Chrome
的UI
界面都选择采用GPU
来绘制,这使得GPU
成为浏览器普遍的需求。最后,Chrome
在其多进程架构上也引入了GPU
进程。 - 网络进程 。主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
- 插件进程 。主要是负责插件的运行,因插件易崩溃,所以需要通过插件进程来隔离,以保证插件进程崩溃不会对浏览器和页面造成影响
了解了 Chrome
的多进程架构,就能够从宏观上理解从输入 URL
到页面渲染的过程了,这个过程主要分为 导航阶段 和 渲染阶段 。
二、导航阶段
Ⅰ.浏览器主进程
1.用户输入 URL
- 1、 浏览器进程检查
url
,组装协议,构成完整的url
,这时候有两种情况:- 输入的是搜索内容:地址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的
URL
。 - 输入的是请求
URL
:地址栏会根据规则,给这段内容加上协议,合成为完整的URL
;
- 输入的是搜索内容:地址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的
- 2、 浏览器进程通过进程间通信(
IPC
)把url
请求发送给网络进程;
Ⅱ.网络进程
2. URL
请求过程
- 3、 网络进程接收到
url
请求后检查本地缓存是否缓存了该请求资源,如果有则将该资源返回给浏览器进程;
这里涉及到浏览器的缓存策略问题,有兴趣的可以上网查阅相关资料。
-
4、准备
IP
地址和端口:进行DNS
解析时先查找缓存,没有再使用DNS
服务器解析,查找顺序为:- 浏览器缓存;
- 本机缓存;
-
hosts
文件; - 路由器缓存;
-
ISP DNS
缓存; -
DNS
递归查询(本地DNS
服务器 -> 权限DNS
服务器 -> 顶级DNS
服务器 ->13
台根DNS
服务器)
-
5、等待
TCP
队列:浏览器会为每个域名最多维护6
个TCP
连接,如果发起一个HTTP
请求时,这6
个TCP
连接都处于忙碌状态,那么这个请求就会处于排队状态;解决方案:- 采用域名分片技术:将一个站点的资源放在多个(
CDN
)域名下面。 - 升级为
HTTP2
,就没有6
个TCP
连接的限制了;
- 采用域名分片技术:将一个站点的资源放在多个(
-
6、通过三次握手建立
TCP
连接:- 第一次: 客户端先向服务器端发送一个同步数据包,报文的
TCP
首部中:标志位: 同步SYN
为1
,表示这是一个请求建立连接的数据包;序号Seq=x
,x
为所传送数据的第一个字节的序号,随后进入SYN-SENT
状态;
标志位值为
1
表示该标志位有效。- 第二次: 服务器根据收到数据包的
SYN
标志位判断为建立连接的请求,随后返回一个确认数据包,其中标志位SYN=1
,ACK=1
,序号seq=y
,确认号ack=x + 1
表示收到了客户端传输过来的x
字节数据,并希望下次从x+1
个字节开始传,并进入SYN-RCVD
状态;
这里要区分标志位
ACK
和确认号ack
;- 第三次: 客户端收到后,再给服务器发送一个确认数据包,标志位
ACK=1
,序号seq=x+1
,确认号ack=y+1
,随后进入ESTABLISHED
状态;
服务器端收到后,也进入
ESTABLISHED
状态,由此成功建立了TCP
连接,可以开始数据传送;- 为什么要第三次挥手? 避免服务器等待造成 资源浪费 ,具体原因:
如果没有最后一个数据包确认(第三次握手),
A
先发出一个建立连接的请求数据包,由于网络原因绕远路了。A
经过设定的超时时间后还未收到B
的确认数据包。于是发出第二个建立连接的请求数据包,这次网路通畅,
B
的确认数据包也很快就到达A
。于是A
与B
开始传输数据;过了一会
A
第一次发出的建立连接的请求数据包到达了B
,B
以为是再次建立连接,所以又发出一个确认数据包。由于A已经收到了一个确认数据包,所以会忽略B
发来的第二个确认数据包,但是B
发出确认数据包之后就要一直等待A
的回复,而A
永远也不会回复。由此造成服务器资源浪费,这种情况多了
B
计算机可能就停止响应了。 - 第一次: 客户端先向服务器端发送一个同步数据包,报文的
-
7、构建并发送
HTTP
请求信息; -
8、服务器端处理请求;
-
9、客户端处理响应,首先检查服务器响应报文的状态码:
- 如果是
301/302
表示服务器已更换域名需要重定向,这时网络进程会从响应头的Location
字段里面读取重定向的地址,然后再发起新的HTTP
或者HTTPS
请求,跳回第4
步。 - 如果是
200
,就检查Content-Type
字段,值为text/html
说明是HTML
文档,是application/octet-stream
说明是文件下载;
- 如果是
- 10、 请求结束,当通用首部字段
Conection
不是Keep-Alive
时,即不为TCP
长连接时,通过四次挥手断开TCP
连接:
- 第一次: 客户端(主动断开连接)发送数据包给服务器,其中标志位
FIN=1
,序号位seq=u
,并停止发送数据; - 第二次: 服务器收到数据包后,由于还需传输数据,无法立即关闭连接,先返回一个标志位
ACK=1
,序号seq=v
,确认号ack=u+1
的数据包; - 第三次: 服务器准备好断开连接后,返回一个数据包,其中标志位
FIN=1
,标志位ACK=1
,序号seq=w
,确认号ack=u+1
; - 第四次: 客户端收到数据包后,返回一个标志位
ACK=1
,序号seq=u+1
,确认号ack=w+1
的数据包。
由此通过四次挥手断开 TCP
连接。
详细过程参见: 详解TCP连接的“三次握手”与“四次挥手”(上)
- 为什么要四次挥手? 由于服务器不能马上断开连接,导致
FIN
释放连接报文与ACK
确认接收报文需要分两次传输,即第二次和第三次"挥手";
3.准备渲染进程
- 11、 准备渲染进程:浏览器进程检查当前
url
是否与之前打开了渲染进程的页面的根域名相同,如果相同,则复用原来的进程,如果不同,则开启新的渲染进程;
4.提交文档
- 12、 提交文档:
- 渲染进程 准备好后, 浏览器 向 渲染进程 发起“ 提交文档 ”的消息, 渲染进程 接收到消息后与 网络进程 建立传输数据的“ 管道 ”
- 渲染进程 接收完数据后,向浏览器发送“ 确认提交 ”
- 浏览器进程 接收到确认消息后更新浏览器界面状态: 安全状态 、 地址栏
url
、 前进后退的历史状态 、 更新web
页面
三、渲染阶段
在渲染阶段通过 渲染流水线 在渲染进程的主线程和合成线程配合下,完成页面的渲染;
Ⅲ.渲染进程
渲染进程中的主线程部分
5.构建 DOM
树
-
13、先将请求回来的数据解压,随后
HTML
解析器将其中的HTML
字节流 通过 分词器 拆分为一个个Token
,然后生成节点Node
,最后解析成浏览器识别的DOM
树结构。可以通过
Chrome
调试 工具 的Console
选项打开控制台输入document
查看DOM
树;
渲染引擎还有一个 安全检查模块 叫 XSSAuditor
,是用来 检测词法安全 的。在分词器解析出来 Token
之后,它会检测这些模块是否安全,比如 是否引用了外部脚本 , 是否符合 CSP
规范 , 是否存在跨站点请求 等。如果出现不符合规范的内容, XSSAuditor
会对该脚本或者下载任务 进行拦截 。
首次解析 HTML
时 渲染进程 会开启一个 预解析线程 ,遇到 HTML
文档中内嵌的 JavaScript
和 CSS
外部引用就会同步提前下载这些文件,下载时间以最后下载完的文件为准。
6.构建 CSSOM
-
14、
CSS
解析器将CSS
转换为浏览器能识别的styleSheets
也就是CSSOM
:可以通过控制台输入document.styleSheets
查看;这里要考虑一下阻塞的问题,由于
JavaScript
有修改CSS
和HTML
的能力,所以,需要先等到CSS
文件下载完成并生成CSSOM
,然后再执行JavaScript
脚本,最后再继续构建DOM
。由于这种阻塞,导致了 解析白屏 ;
优化方案:
- 移除
js
和css
的文件下载 :通过内联JavaScript
、内联CSS
; - 尽量减少文件大小 :如通过
webpack
等工具 移除 不必要的 注释 ,并 压缩js
文件 ; - 将不进行
DOM
操作或CSS
样式修改的JavaScript
标记上sync
或者defer
异步引入; - 使用媒体查询属性 :将大的
CSS
文件拆分成多个不同用途的CSS
文件,只有在特定的场景下才会加载特定的CSS
文件。
可以通过浏览器调试工具的 Network
面板中的 DOMContentLoaded
查看最后生成 DOM
树所需的时间;
7.样式计算
- 15、 转换样式表中的属性值,使其标准化。比如将
em
转换为px
,color
转换为rgb
; - 16、 计算
DOM
树中每个节点的具体样式,这里遵循CSS
的继承和层叠规则;可以通过Chrome
调试工具的Elements
选项的Computed
查看某一标签的最终样式;
8.布局阶段
-
17、创建布局树,遍历
DOM
树中的所有节点,去掉所有隐藏的节点(比如head
,添加了display:none
的节点),只在布局树中保留可见的节点。 -
18、计算布局树中节点的坐标位置(较复杂,这里不展开);
9.分层
- 19、 对布局树进行分层,并生成分层树(
Layer Tree
),可以通过Chrome
调试工具的Layer
选项查看。分层树中每一个节点都直接或间接的属于一个图层(如果一个节点没有对应的层,那么这个节点就从属于父节点的图层)
10.图层绘制
- 20、 为每个图层生成绘制列表(即绘制指令),并将其提交到合成线程。以上操作都是在渲染进程中的主线程中进行的,提交到合成线程后就不阻塞主线程了;
渲染进程中的合成线程部分
11.切分图块
21、合成线程将图层切分成大小固定的图块( 256x256
或者 512x512
)然后 优先绘制 靠近视口的图块,这样就可以大大加速页面的显示速度;
Ⅳ. GPU
进程
12.栅格化操作
- 22、 在 光栅化线程池 中将 图块 转换成 位图 ,通常这个过程都会使用
GPU
来加速生成,使用GPU
生成位图的过程叫 快速栅格化 ,或者GPU
栅格化,生成的位图被保存在GPU
内存中。
Ⅴ.浏览器主进程
13.合成与显示
- 23、 合成:一旦所有图块都被光栅化, 合成线程 就会将它们合成为一张图片,并生成一个绘制图块的命令——“
DrawQuad
”,然后将该命令提交给浏览器进程。
注意了:合成的过程是在渲染进程的 合成线程 中完成的,不会影响到渲染进程的 主线程 执行;
- 24、 显示:浏览器进程里面有一个叫
viz
的组件,用来接收合成线程发过来的DrawQuad
命令,然后根据DrawQuad
命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
到这里,经过这一系列的阶段,编写好的 HTML
、 CSS
、 JavaScript
等文件,经过浏览器就会显示出漂亮的页面了。
参考资料: 浏览器工作原理与实践
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Octane渲染入门-渲染设置图文版
- 通过分析 WPF 的渲染脏区优化渲染性能
- React 服务器端渲染和客户端渲染效果对比
- iOS渲染-将视频原始数据(RGB,YUV)渲染到屏幕上
- 通过共享内存优化 Flutter 外接纹理的渲染性能,实时渲染不是梦
- 列表渲染 wx:key 的作用、条件渲染 wx:if 与 hidden 的区别
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。