前言:
相信很多小伙伴和我一样在面试中总是会被问到关于浏览器的方方面面的问题,,但是面试的时候可能总是不知道如何系统的回答这个问题!!! 说实话我们平时没少和浏览器打交道,但是你真的了解透了它吗?作为前端需要对它了解到哪种程度呢?
一、首先来一道高频面试题:从输入URL到页面显示内容,这中间发生了什么?
相信大家肯定都被问过这个问题,大家看到这个可以自己先思考一下自己的答案,这到题考查的是大家对前端各方面知识的理解和认知,当然回答的越具体越好。
1.首先是用户输入
- 首先是在浏览器的地址栏输入一个查询关键字时,地址栏会判断输入的关键字是搜索内容还是需要请求的URL。
- 如果是搜索内容的话浏览器会使用默认的搜索引擎来合成新的带搜索关键字的url.
- 如果符合url规则,那么地址栏会根据相应的规则,把这段内容加上协议合成完整的url. 如:www.baidu.com/s?ie=UTF-8&…
- 在老页面被替换新的页面之前,浏览器还给了当前页面一直执行 beforeunload 事件的机会,在当前页面监听了 beforeunload 事件时,允许页面在退出之前执行一些数据清理和询问用户是否需要离开当前页面。 然后页面进入加载阶段
2.url请求过程
接下来便进入了页面资源请求的过程。这里我们需要知道一个概念就是浏览器的进程分类。 看下图:
- 浏览器进程主要负责的就是界面显示、用户交互、子进程管理,同时提供储存功能
- 渲染进程:核心任务是将HTML,css和JavaScript转换为可供用户操作的网页
- GPU进程:主要是用来绘制UI界面和实现3D css 的效果
- 网络进程:主要是负责页面的网络资源的加载
- 插件进程:这个主要是运行页面中的插件,不一定每个页面都有
好了,有了以上知识背景之后我们来看看这一步具体是怎样进行的。
-
首先浏览器进程会通过进程间的通行(IPC)把url请求发送给网络进程,然后网络进程会发起真正的url请求流;
-
然后网络进程会去查找本地是否缓存了该资源,如果有缓存,那么直接返回给浏览器进程(这里涉及到浏览器缓存的相关知识,后面会有讲解),如果没有找到缓存,则直接进入网络请求流程。
-
第一步是DNS解析,以便获取域名服务器的IP,如果请求的协议是HTTPS则还需要建立TLS连接;
-
接下来就是利用IP和服务器建立tcp连接(通过三次握手,后面也会有讲解),建立完连接之后,浏览器会构建请求行、请求头等信息,并且把该域名下的cookie等数据附加到请求头之中,然后向服务器发送构建完成的请求信息;
-
服务器接收到请求信息后,会根据请求信息生成响应数据,并且发送给网络进程,在网络进程接收到后就开始解析响应头的内容了
-
这里假如接收到服务器返回的状态码是301或者302,说明服务器需要浏览器重定向到其他url,需要定位的url是放在响应头的Location字段里,这时候一切又从头开始了。
-
如果服务器返回的状态码是200则说明一切正常,浏览器可以正常的往下执行了。
有时候url的请求类型是一个下载类型,有时候是正常的HTML类型,那么要怎么区分它们呢?
-
浏览器是通过响应头中的content-type字段来区分的,如果字段的值是text/html,就是告诉浏览器这是一个html格式的文件,你需要安装html的格式来解析,然后就需要准备渲染过程了;
-
如果字段的值是application/octet-stream则表示数据是字节类型的,通常情况下浏览器会按照下载类型来处理,该请求就会被交于浏览器的下载管理器,url的请求流程就结束了。
3.准备渲染过程
这个过程简单来说就是浏览器根据当前页面的情况来分配渲染进程的过程; 这里会区分2种情况:
- 第一种是当前打开的页面和新页面是属于同一站点的话会复用父页面的渲染进程(同一站点的定义是根域名和协议是相同的)。
- 另外一种是当前打开的页面和新页面不属于同一站点,这种情况下浏览器会为新页面开启一个新的渲染进程
4.提交文档
这个过程就是指,浏览器进程将网络进程接收到的html数据提交给渲染进程的一个过程,这里面也分几个步骤:
- 当浏览器进程接收到网络进程的响应头之后,便向渲染进程发起“提交文档”的指令;
- 渲染进程接收到浏览器进程的“提交文档”的指令后,会和网络进程建立起一个连接;
- 文档数据传输完成之后,渲染进程会返回一个“确认提交”的指令给浏览器进程,浏览器进程接收到之后就开始更新界面状态
5.渲染过程
文档一旦被提交,渲染进程就会开始页面解析和子资源的加载了,这里面的步骤比较多,大家也都相对来说比较熟悉
a.构建DOM树
因为浏览器是读不懂html的,所以这里需要将html转化为浏览器能够读懂的结构--DOM树结构,转化的过程是通过词法分析,语法分析来完成的;
你可以在控制台里输入'document'(如下图)
b.样式的计算
样式计算的目的是为了计算出DOM节点中的每个元素的具体样式
- 第一步,把css转换为浏览器能够理解的结构--样式表(styleSheets),你可以通过在控制台输入'document.styleSheets',然后就可以看到浏览器把全部的样式转换完的样式表(styleSheets)
- 第二步,转换样式表中的属性值,使其标准化,将浏览器识别不了的一些属性值转换成标准的值;比如:em red bold这些属性值都会被转换
- 第三步,计算出DOM树中每个节点的具体样式,这里就会涉及到css的继承规则和层叠规则;继承规则很好理解,就是每个DOM节点都会包含父节点的样式信息;层叠是css的一个基本特征,它是一个定义了如何合并多个源的属性值的算法。
样式计算阶段的目的是为了计算出DOM节点中的每个元素的具体样式,在计算的过程中会遵循css的继承和层叠2个规则,最后输出的内容是每个DOM节点的样式
c.布局阶段
当我们有了DOM树和DOM节点对应的样式之后还不足以显示页面,因为我们还不知道DOM元素的几何位置,接下来我们就需要去计算出DOM树中可见节点的几何位置,这个计算的过程就叫做布局。 布局阶段分为2个步骤:
- 第一步,创建布局树,这个过程就是将DOM树中的可见元素过滤出来,额外的构建一个只包含DOM树中的可见元素的布局树结构
- 第二步,布局计算,在有了一个完整的布局树之后,就是需要去计算布局树节点的坐标位置了。
d.分层
在绘制页面之前还需要几个步骤: 首先是分层,因为页面中有很多复杂的效果,如3D变换,页面滚动,z-index的Z轴排序等;为了更好的实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一颗对应的图层树。(就好比ps里面的图层的概念)
浏览器的页面其实就是被划分成多个图层,然后最终将这些图层层叠到一起,就合成了最终的页面。
通常情况下并不是每一个布局树的节点都对应一个图层,如果一个节点没有对应的图层,那么它就是属于父节点的图层。
这里就会有一个规则,什么情况下渲染引擎才会为特定的节点创建一个新的图层呢?
- 首先是拥有层叠上下文属性的元素会被提到单独的一层,就是Z轴上的排序(position: fixed z-index: 2 filter:blue(5px) opacity:0.5)
- 然后还有需要**剪裁(clip)**的地方也会创建一个图层
e.图层绘制
图层绘制这个比较好理解,就是先把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表,所以在图层绘制阶段输出的内容就是这些待绘制列表。
f.栅格化
当图层绘制列表准备好之后,渲染进程的主线程会把绘制列表commit给合成线程;
通常我们的页面可能会比较大,用户一般都只能看到其中的一部分,我们可以把用户看到的这一部分称作 视口(viewport)
为了提升渲染性能合成线程会将图层划分为图块,一般图块的大小是 256*256 或者 512*512
g.合成和显示
一旦所有的图块都栅格化,合成线程就会向浏览器进程发送一个绘制图块的指令"DrawQuad",浏览器进程有一个叫viz的组件,用来接收合成线程发过来的"DrawQuad"指令,然后根据"DrawQuad"指令将器页面绘制到内存中。最后再将内存显示在屏幕上。
二、重排和重绘和合成
重排:是指更改了元素的几何属性
下图表示了这个过程:
重绘:是指更新了元素的绘制属性
下图表示了这个过程:
直接合成阶段
如果是改变一个既不需要重排也不需要重绘的属性,浏览器会跳过布局和绘制,只执行合成的操作,流程参考下图:
.
.
参考资料:《极客时间》-李兵老师的 浏览器工作原理与实践
.
(持续更新)