页面显示全过程
作者:互联网
页面显示全过程
从输入 URL 到页面展示,这中间发生了什么?
从输入URL到页面展示,这中间发生了什么?(一)
一、浏览器进程发出URL请求给网络进程
当用户在地址栏中输入一个查询关键字时,地址栏会判断输入的关键字是搜索内容,还是请求的 URL。
如果是搜索内容,地址栏会使用浏览器默认的搜索引擎,来合成新的带搜索关键字的 URL。
如果判断输入内容符合 URL 规则,比如输入的是 time.geekbang.org,那么地址栏会根据规则,把这段内容加上协议,合成为完整的 URL,如 https://time.geekbang.org。
当用户输入关键字并键入回车之后,浏览器还给了当前页面一次执行 beforeunload 事件的机会,beforeunload 事件允许页面在退出之前执行一些数据清理操作,还可以询问用户是否要离开当前页面,比如当前页面可能有未提交完成的表单等情况,因此用户可以通过 beforeunload 事件来取消导航,让浏览器不再执行任何后续工作。
当前页面没有监听 beforeunload 事件或者同意了,则继续走后续流程
当浏览器刚开始加载一个地址之后,标签页上的图标便进入了加载状态。但此时图中页面显示的依然是之前打开的页面内容,并没立即替换。
二、网络进程接收到URL请求之后,便发起网络请求,然后服务器返回HTTP数据到网络进程,网络进程解析HTTP响应行和响应头数据,将结果发给浏览器进程
首先,网络进程会查找本地缓存是否缓存了该资源。如果有缓存资源,那么直接返回资源给浏览器进程;如果在缓存中没有查找到资源,那么直接进入网络请求流程。这请求前的第一步是要进行 DNS 解析,以获取请求域名的服务器 IP 地址。如果请求协议是 HTTPS,那么还需要建立 TLS 连接。
接下来就是利用 IP 地址和服务器建立 TCP 连接。连接建立之后,浏览器端会构建请求行、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。
服务器接收到请求信息后,会根据请求信息生成响应数据(包括响应行、响应头和响应体等信息),并发给网络进程。等网络进程接收了响应行和响应头之后,就开始解析响应头的内容了。(为了方便讲述,下面我将服务器返回的响应头和响应行统称为响应头。)
①重定向
在接收到服务器返回的响应头后,网络进程开始解析响应头,如果发现返回的状态码是 301 或者 302,那么说明服务器需要浏览器重定向到其他 URL。这时网络进程会从响应头的 Location 字段里面读取重定向的地址,然后再发起新的 HTTP 或者 HTTPS 请求,一切又重头开始了
如果服务器返回的响应头的状态码是 200,这是告诉浏览器一切正常,可以继续往下处理该请求了。
②Content-Type
不同 Content-Type 的后续处理流程也截然不同。如果 Content-Type 字段的值被浏览器判断为下载类型,那么该请求会被提交给浏览器的下载管理器,同时该 URL 请求的导航流程就此结束。但如果是 HTML,那么浏览器则会继续进行导航流程。由于 Chrome 的页面渲染是运行在渲染进程中的,所以接下来就需要准备渲染进程了。
三、准备渲染进程
打开一个新页面采用的渲染进程策略就是:
通常情况下,打开新的页面都会使用单独的渲染进程;
如果从 A 页面打开 B 页面,且 A 和 B 都属于同一站点的话,那么 B 页面复用 A 页面的渲染进程;如果是其他情况,浏览器进程则会为 B 创建一个新的渲染进程。
渲染进程准备好之后,还不能立即进入文档解析状态,因为此时的文档数据还在网络进程中,并没有提交给渲染进程,所以下一步就进入了提交文档阶段。
四、假设渲染进程已经准备好,则浏览器进程发送 CommitNavigation(提交导航)消息到渲染进程
五、渲染进程接收到CommitNavigation消息之后,直接和网络进程建立数据管道,进行数据传输
六、数据传输完毕之后,渲染进程会向浏览器进程返回“确认提交”
七、最后浏览器进程更新页面状态,完整的导航流程结束
浏览器进程在收到“确认提交”的消息后,会更新浏览器界面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 Web 页面
到这里,一个完整的导航流程就“走”完了,这之后就要进入渲染阶段了
导航流程完成之后,就来到了我们的渲染流程,渲染流程分为以下七个步骤
一、构建dom树
浏览器无法直接理解和使用 HTML,所以需要将 HTML 转换为浏览器能够理解的结构——DOM 树。
在控制台里面输入“document”后回车,这样你就能看到一个完整的 DOM 树结构
也可以通过JavaScript查询或修改dom树
二、样式计算
1、把 CSS 转换为浏览器能够理解的结构
和 HTML 文件一样,浏览器也是无法直接理解这些纯文本的 CSS 样式,所以当渲染引擎接收到 CSS 文本时,会执行一个转换操作,将 CSS 文本转换为浏览器可以理解的结构——styleSheets。
在控制台中输入 document.styleSheets,这样你就能看到一个完整的styleSheets结构
2、 转换样式表中的属性值,使其标准化
CSS 文本中有很多属性值,如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。
body { font-size: 2em }
p {color:blue;}
span {display: none}
div {font-weight: bold}
div p {color:green;}
div {color:red; }
2em 被解析成了 32px,red 被解析成了 rgb(255,0,0),bold 被解析成了 700……
3、计算出 DOM 树中每个节点的具体样式
涉及到 CSS 的继承规则和层叠规则了。
首先是 CSS 继承。CSS 继承就是每个 DOM 节点都包含有父节点的样式。
然后是层叠。层叠是 CSS 的一个基本特征,它是一个定义了如何合并来自多个源的属性值的算法。它在 CSS 处于核心地位,CSS 的全称“层叠样式表”正是强调了这一点。
总之,样式计算阶段的目的是为了计算出 DOM 节点中每个元素的具体样式,在计算过程中需要遵守 CSS 的继承和层叠两个规则。这个阶段最终输出的内容是每个 DOM 节点的样式,并被保存在 ComputedStyle 的结构内。
三、布局阶段
1、创建布局树
DOM 树还含有很多不可见的元素,比如 head 标签,还有使用了 display:none 属性的元素。所以在显示之前,我们还要额外地构建一棵只包含可见元素布局树。
为了构建布局树,浏览器大体上完成了下面这些工作:
遍历 DOM 树中的所有可见节点,并把这些节点加到布局树中;
而不可见的节点会被布局树忽略掉,如 head 标签下面的全部内容,再比如 body.p.span 这个元素,因为它的属性包含 dispaly:none,所以这个元素也没有被包进布局树。
2、 布局计算
接着又计算每个元素的几何坐标位置,并将这些信息保存在布局树中。
四、分层
分层就是生成图层树
因为页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层,并生成一棵对应的图层树(LayerTree)
不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层
那么需要满足什么条件,渲染引擎才会为特定的节点创建新的图层呢?
通常满足下面两点中任意一点的元素就可以被提升为单独的一个图层。
拥有层叠上下文属性的元素会被提升为单独的一层
需要剪裁(clip)的地方也会被创建为图层。
五、图层绘制
图层绘制就是为每一个图层生成绘制列表(渲染引擎的主线程不能把文档画到浏览器上去,而是生成绘制列表,交给合成线程)
渲染引擎会把一个图层的绘制拆分成很多小的绘制指令,然后再把这些指令按照顺序组成一个待绘制列表
绘制列表中的指令其实非常简单,就是让其执行一个简单的绘制操作,比如绘制粉色矩形或者黑色的线等。而绘制一个元素通常需要好几条绘制指令,因为每个元素的背景、前景、边框都需要单独的指令去绘制。所以在图层绘制阶段,输出的内容就是这些待绘制列表。
可以打开“开发者工具”的“Layers”标签,选择“document”层,来实际体验下绘制列表
六、栅格化(raster)操作
绘制列表只是用来记录绘制顺序和绘制指令的列表,而实际上绘制操作是由渲染引擎中的合成线程来完成的。
当图层的绘制列表准备好之后,主线程会把该绘制列表提交(commit)给合成线程
合成线程是怎么工作的呢?
在有些情况下,有的图层可以很大,比如有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下,要绘制出所有图层内容的话,就会产生太大的开销,而且也没有必要。
合成线程会将图层划分为图块(tile)
图块简单来说就是图层叠加到一块
然后合成线程会按照视口附近的图块来优先生成位图(就是视口矩形的高上下延申出一部分生成位图)
实际生成位图的操作是由栅格化来执行的。所谓栅格化,是指将图块转换为位图。而图块是栅格化执行的最小单位。渲染进程维护了一个栅格化的线程池,所有的图块栅格化都是在线程池内执行的
通常,栅格化过程都会使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。
如果栅格化操作使用了 GPU,那么最终生成位图的操作是在 GPU 中完成的,这就涉及到了跨进程操作
渲染进程把生成图块的指令发送给 GPU,然后在 GPU 中执行生成图块的位图,并保存在 GPU 的内存中。
七、合成和显示
一旦所有图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”,然后将该命令提交给浏览器进程。
浏览器进程里面有一个叫 viz 的组件,用来接收合成线程发过来的 DrawQuad 命令,然后根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。
到这里,经过这一系列的阶段,编写好的 HTML、CSS、JavaScript 等文件,经过浏览器就会显示出漂亮的页面了。
标签:显示,浏览器,渲染,全过程,进程,图层,绘制,页面 来源: https://blog.csdn.net/weixin_43750611/article/details/120238788