文章背景:
当用户在地址栏中输入一个url到页面的完全渲染完成,我这边想从浏览器都做了哪些工作,来清晰的描述出来,让小伙伴都能清楚浏览器背后所做的操作,也是浏览器工作原理之一吧
开始探索:
当我们打开一个页面,会启动哪些进程?
当我们打开一个新的页面,浏览器至少需要加载四个进程:1个浏览器进程,1个渲染进程,1个网络进程,1个GPU进程。
浏览器进程:主要负责界面显示、用户交互、子进程管理,同时提供存储等功能。
渲染进程: 核心任务是将 HTML、CSS 和 JavaScript 转换为用户可以与之交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 都是运行在该进程中,默认情况下,Chrome 会为每个 Tab 标签创建一个渲染进程。出于安全考虑,渲染进程都是运行在沙箱模式下。
网络进程:主要负责页面的网络资源加载,之前是作为一个模块运行在浏览器进程里面的,直至最近才独立出来,成为一个单独的进程。
GPU 进程:其实,Chrome 刚开始发布的时候是没有 GPU 进程的。而 GPU 的使用初衷是为了实现 3D CSS 的效果,只是随后网页、Chrome 的 UI 界面都选择采用 GPU 来绘制,这使得 GPU 成为浏览器普遍的需求。最后,Chrome 在其多进程架构上也引入了 GPU 进程。
那既然打开一个页面必须要这四个进程,也是靠着四个进程的相互合作才能把页面给渲染完毕
下面有张图,介绍了一个url从输入到页面展示,所做的工作的一个流程图
结合这张图,来详细的解释一下
1.当我们输入完url后,会先去构建请求
浏览器先构建请求行信息,构建好后,浏览器准备发起网络请求
请求行:
GET /index.html HTTP1.1
2.查找缓存
网络进程接收到URL请求后,会查找本地缓存是否缓存了该资源,
如果有缓存资源,那么直接返回资源给浏览器进程,
如果没查找到资源,那么直接进入网络请求流程,
请求前的第一步是要进行DNS解析,获取请求域名的服务器IP地址,如果请求协议是https,还需要建立TLS连接
3.准备IP地址和端口号
在这之前我们需要http和tcp之间的关系
http协议:应用层协议,用来封装请求的文本信息
tcp协议:传输层协议,将其发到网络上
在http工作之前,需要通过tcp与服务器建立连接,http的内容是通过TCP的传输数据阶段来实现的
我们可以通过下面的一张图来看清TCP和HTTP的关系
http网络请求的第一步是做什么呢?是需要先和服务器建立TCP链接。
TCP连接的第一步是需要准备IP地址和端口号
ip地址和端口号怎么拿呢?通过DNS(domain name system),DNS就是把域名和IP地址做了一 一映射的关系
通过DNS来获取到IP地址和端口号
4.等待TCP队列
chrome 有个机制,同一个域名只能建立6个TCP连接,如果超过6个,就需要等待
5.建立TCP连接
如果需要等待的话,就等待,无的话,就直接进入到建立TCP的连接,如果对TCP不了解的话,我先介绍一下
TCP:是一种面向连接的、可靠的、基于字节流的传输层通信协议,TCP的头部包含了目标端口和本机端口,还有排序机制
那么 TCP与服务器建立连接,是怎么连接的呢?
一个完整的TCP连接的生命周期包括了,建立连接、传输数据、断开连接,如下图所示
建立连接阶段: 通过‘三次握手’来建立客户端和服务器之间的连接 三次握手:在建立一个TCP连接时,客户端和服务器总共要发送三个数据包以确认连接的建立
传输阶段: 在该阶段,接收端需要对每个数据包进行确认操作,也就是接收端在接收到数据包之后,需要发送确认数据包给发送端。所以当发送端发送了一个数据包之后,在规定时间内没有接收到接收端反馈的确认消息,则判断为数据包丢失,并触发发送端的重发机制。同样,一个大的文件在传输过程中会被拆分成很多小的数据包,这些数据包到达接收端后,接收端会按照 TCP 头中的序号为其排序,从而保证组成完整的数据。
断开连接: 数据传输完毕之后,就要终止连接了,通过四次挥手,来保证双方都能断开连接
6.发送http请求
一旦建立了tcp连接,浏览器就可以和服务器通信了,http的数据正是在这个通信过程中传输的
浏览器是如何发送请求信息给服务器的?
首先会向服务器发送请求行,它包括了请求方法、请求url和http版本协议
请求行:是为了告诉服务器,我们用什么方式请求,请求的地址是什么,协议的版本
在发送完请求行命令之后,还要以请求头形式发送一些其它信息,把浏览器的一些基础信息告诉服务器,比如:浏览器的操作系统,浏览器内核,域名信息,浏览器的cookie信息,post常用请求体
7.服务器端处理http请求流程
http的请求信息,被送到了服务器,服务器返回相应的内容,返回请求
首先返回响应行,包括协议版本和状态码
接着返回响应头和响应体
响应数据类型处理:浏览器是如何区分URL请求的数据类型是下载类型还是HTML类型?
用Content-Type:content-type是http头中一个非常重要的字段,它告诉浏览器服务器返回的响应体数据是什么类型,浏览器根据这个值来决定如何显示响应体的内容
一般情况下,一旦服务器向客户端返回了请求数据,它就要关闭TCP连接,断开连接
不过如果在浏览器或者服务器头部信息中加入了
Connection:Keep-Alive
tcp连接在发送后将仍然保持打开状态,这样浏览器就可以继续通过同一个TCP连接发送请求,保持TCP连接可以省去下次请求时需要建立连接的时间,提升资源加载速度
http请求从发起到结束一共经历了九个阶段: 构建请求、查找缓存、准备IP和端口、等待TCP队列、建立TCP连接、发起http请求、服务器处理请求、服务器返回请求和断开连接
8.接收请求
网络进程接收到了响应头的数据,便开始解析响应头数据,并将数据转发给浏览器进程
浏览器进程接收到网络进程的响应头数据之后,发送‘发送提交导航’消息到渲染进程
渲染进程接收到‘提交导航’的消息之后,便开始准备接收HTML数据,接收数据的方式是直接和网络进程建立数据管道
浏览器进程接收到渲染进程‘提交文档’的消息之后,便开始移除之前旧的文档,会更新浏览器界面状态,包括了安全状态、地址栏的URL、前进后退的历史状态,并更新web页面,如下图所示:
准备渲染进程
通常情况下chrome会每个页面分配一个渲染进程,每打开一个新页面就会配套创建一个新的渲染进程,但是是同一站点的情况下,新页面会复用父页面的渲染进程。 ( 同一站点:根域名和协议一样的站点)
9.渲染阶段
编写好的html、css等文件,是如何转化成页面的?
按照渲染的时间顺序,可分为以下几个子阶段:构建DOM树、样式计算、布局阶段、分层、绘制、分块、光栅化和合成
9.1构建DOM树
为什么要构建DOM树?因为浏览器无法直接理解和使用HTML。所以需要将HTML转换为浏览器能够理解的结构--DOM树
HTML文件经由HTML解析器解析,最终输出树状结构的DOM
9.2样式计算
计算的目的是为了计算出DOM节点中每个元素的具体样式,这个阶段大体可分为三步来完成
9.2.1 把css转换为浏览器能够理解的结构
当渲染引擎接收到css文本时,会执行一个转换操作,将css文本转换为浏览器可以理解的结构--styleSheets
9.2.2 转换样式表中的属性值,使其标准化
需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程属于属性值标准化
比如:2em的转化为32px red转化为rgb(255,0,0)
9.2.3 计算出DOM中每个节点的具体样式
在计算过程中需要遵守css的继承和层叠两个规则,这个阶段最终输出的内容是每个DOM节点的样式,并被保存在ComputedStyle的结构内
9.3布局阶段
遍历DOM树种的所有可见节点,并把这些节点加到布局树中;
而不可见的节点会被布局树忽略掉
布局计算
计算布局树节点的坐标位置,运算完会把结果再重新写回到布局树中,最终合成上面的一个布局树
9.4 分层
在有了布局树之后,每个元素的具体位置都已经计算出来了,接下来要进行分层
渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree),浏览器的页面被分成了很多图层,这些图层叠加合成了最终的页面, 并不是每个节点都包含一个图层,如果一个节点没有对应的层,就从属于父节点的图层
9.5 图层绘制
在完成图层树的构建之后,渲染引擎会对每个图层进行绘制,会把一个图层绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表,如下图所示
也可以打开‘开发者工具’的layers标签,可以看出
9.6分块
当图层的绘制列表准备好之后,主线程会把该绘制列表提交给合成线程,
合成线程会将图层划分成图块
9.7 栅格化操作
合成线程会按照视口附近的图块来优先生成位图,实际生成位图的操作是有栅格化来执行的,所谓栅格化,是指将图块转换为位图。
图块是栅格化执行的最小单位,渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的,运行方式如下图所示
通常,栅格化过程都会使用GPU来加速生成,使用GPU生成位图的过程叫快速栅格化,或者GPU栅格化,生成的位图被保存在GPU内存中
9.8合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令--‘drawQuad’,然后将该命令提交给浏览器进程。
浏览器进程里面有一个叫viz的组件,用来接收合成线程发过来的DrawQuad命令,然后根据DrawQuad命令,将其页面内容绘制到内存中,最后再讲内存显示在屏幕上。
经过这一系列的阶段,编写好的HTML、CSS,经过浏览器就显示出页面了
渲染阶段总结一下
从HTML到DOM 、样式计算、布局、分层、图层绘制、分块、光栅化、合成和显示,整个渲染的过程
一个完整的渲染流程大致可为如下
1.渲染进程将HTML内容转换为能够读懂的DOM树结构
2.渲染引擎将css样式表转化为浏览器可以理解的styleSheets,计算出DOM节点的样式。
3.创建布局树,并计算元素的布局信息
4.对布局树进行分层,并生成分层树
5.为每个图层生成绘制列表,并将其提交到合成线程
6.合成线程将图层分为图块,并在光栅化线程池中将图块转换成位图
7.合成线程发送绘制图块命令DrawQuad给浏览器进程
8.浏览器进程根据DrawQuad消息生成页面,并显示出来
总结:
以上便是从输入URL到页面展示,浏览器所做的一个漫长的过程,当我们对整个流程都非常清晰的话, 我们对浏览器和html和css又有了一个不一样的认识,当然本文章是对整个大致的流程的一个介绍,在上面的每一步中,都还做了很多的工作,这就接触到浏览器的底层知识了,感兴趣的小伙伴还可以继续对此进行探索