你真的知道从输入 URL 到页面展示发生了什么吗?

161 阅读10分钟

前言:你真的知道从输入 URL 到页面展示发生了什么吗?

导航渲染流程

img

从输入到页面渲染这个过程其实可以说得非常复杂,包括了两大步骤,一个是导航流程另一个是渲染流程;


导航流程

1.用户输入 URL

不考虑用户输入搜索关键字的情况, 如果用户输入的内容符合 URL 规则,浏览器就会根据 URL 协议,在这段内容上加上协议合成合法的 URL

2.loading 状态

用户输入完内容,按下回车键,浏览器导航栏显示loading状态,但是页面还是呈现前一个页面,这是因为新页面的响应数据还没有获取到。

3.浏览器请求

浏览器进程浏览器构建请求行信息,会通过进程间通信(IPC)将 URL 请求发送给网络进程

GET /index.html HTTP1.1

1 2

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字段中附上重定向地址,浏览器会根据codeLocation进行重定向操作;

如果不是重定向,首先服务器会根据 请求头中的 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 树结构:

img

2.Style 样式计算

  • 把 CSS 转换为浏览器能够理解的结构--styleSheets。 这里一 CSDN 为例可以看到它最新的 styleSheets,会包含引用的外部 CSS 文件、标记内的 CSS、以及内嵌的 CSS;img
  • 标准化样式表属性值 将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。 1em 被解析成 1pxred 被解析成了 rgb(255,0,0)等等。
  • 计算 DOM 树每个节点的具体样式 最终的样式可以通过控制台 element 的 Computed 查看,关于是怎么计算,涉及继承规则和层叠规则,这里就不细讲了。img

3.Layout 布局树

  • 创建布局树:遍历 DOM 树中的所有可见节点,加到布局树中。对display:none的就忽略不加。
  • 布局计算:计算布局树节点的坐标位置。

4.layer 图层绘制列表

浏览器的页面实际上被分成了很多图层,这些图层叠加后合成了最终的页面。通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层。

进入 Layer 页面,操作按钮从左至右功能依次为:平移、旋转、复位。见图:img

5.Pain 图层绘制

可以想象你画画,先画远处再画近处,图层绘制也是这种原理。图层绘制阶段,输出待绘制列表。

合成线程

6.栅格化(tiles 图块、raster 光栅化)

在有些情况下,有的图层很大,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下,要绘制所有图层内容的话,就会产生太大的开销,而且也没有必要。

也是因为这个原因,合成线程会将图层进行分块划分为图块然后再栅格化,将图块转换为位图,而渲染进程通常不做或者做不来格栅化,需要跨进程,使用 GPU 来加速生成,使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。

7.Display 合成和显示

图块都被光栅化,合成线程就会生成一个绘制图块的命令——“DrawQuad”提交给浏览器进程。

浏览器进程接收 DrawQuad 命令,根据 DrawQuad 命令,将其页面内容绘制到内存中,最后再将内存显示在屏幕上。

至此页面就被渲染出来了~~完结撒花 ❀❀❀

8.js编译阶段要多个阶段开销很高

Blink(谷歌浏览器的渲染引擎,基于webkit分支开发)主要负责HTML DOM CSS 渲染,嵌入V8引擎,执行js,计算样式和布局,嵌入合成器,绘制图形。

  1. Blink 拿到html代码分析,找到script代码交给V8引擎解析,注意Blink是通过流的形式传给V8的

  2. Scanner(扫描器)首先会进行词法分析

  3. 解析器parser进行语法分析 转化为 AST 抽象语法树

  4. PreParser是预解析器,它的作用是在 JavaScript 代码执行之前对代码进行可选的预处理。预解析器的存在是为了提高代码的执行效率。例如,在一个函数 outer 内部定义了另一个函数 inner,那么 inner 函数就会进行预解析。这意味着在函数 outer 被调用之前,只会对 outer 函数的内容进行解析,而对于 inner 函数的解析会在 outer 函数调用到 inner 函数时才进行。

  5. 解释器Ignition 主要作用就是将AST 抽象语法树 转化成 字节码(bytecode)

为什么要转成字节码而不是直接转成机器码

跨平台执行:不同的硬件架构和操作系统有不同的机器码格式。通过将代码转换为字节码,可以使得同一份字节码在不同的平台上都能执行,实现跨平台的能力。 快速启动和解析:将代码转换为字节码可以比直接生成机器码更快速地进行启动和解析。字节码通常具有更简单的格式和结构,可以更快地被引擎加载和解释执行。 动态优化:现代的JavaScript引擎通常具有即时编译(JIT)功能,可以将热点代码编译成高效的机器码。通过首先将代码转换为字节码,引擎可以更好地进行动态优化和编译,根据实际执行情况生成最优的机器码。这种方式可以在运行时根据代码的实际执行情况进行优化,而不需要提前生成固定的机器码。 代码安全性:字节码作为中间表示形式,可以提供一定的代码安全性。字节码相对于源代码或机器码来说更难以理解和修改,可以提供一定程度的代码保护。

  1. 编译器TurboFan就是将字节码也可以叫中间代码 最后 转换成 机器码 能让我们的CPU识别

渲染流程总结

渲染页面主要做的事:

  • 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 来查询或修改其内容。