# 导航流程:从输入 URL 到页面展示,这中间发生了什么

  1. 用户输入 URL,浏览器会根据用户输入的信息判断是搜索还是网址,如果是搜索内容,就将搜索内容+默认搜索引擎合成新的 URL;如果用户输入的内容符合 URL 规则,浏览器就会根据 URL 协议,在这段内容上加上协议合成合法的 URL
  2. 用户输入完内容,按下回车键,浏览器导航栏显示 loading 状态,但是页面还是呈现前一个页面,这是因为新页面的响应数据还没有获得
  3. 浏览器进程浏览器构建请求行信息,会通过进程间通信(IPC)将 URL 请求发送给网络进程 GET /index.html HTTP1.1
  4. 网络进程获取到 URL,先去本地缓存中查找是否有缓存文件,如果有,拦截请求,直接 200 返回;否则,进入网络请求过程
  5. 网络进程请求 DNS 返回域名对应的 IP 和端口号,如果之前 DNS 数据缓存服务缓存过当前域名信息,就会直接返回缓存信息;否则,发起请求获取根据域名解析出来的 IP 和端口号,如果没有端口号,http 默认 80,https 默认 443。如果是 https 请求,还需要建立 TLS 连接。
  6. Chrome 有个机制,同一个域名同时最多只能建立 6 个 TCP 连接,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。如果当前请求数量少于 6 个,会直接建立 TCP 连接。
  7. TCP 三次握手建立连接,http 请求加上 TCP 头部——包括源端口号、目的程序端口号和用于校验数据完整性的序号,向下传输
  8. 网络层在数据包上加上 IP 头部——包括源 IP 地址和目的 IP 地址,继续向下传输到底层
  9. 底层通过物理网络传输给目的服务器主机
  10. 目的服务器主机网络层接收到数据包,解析出 IP 头部,识别出数据部分,将解开的数据包向上传输到传输层
  11. 目的服务器主机传输层获取到数据包,解析出 TCP 头部,识别端口,将解开的数据包向上传输到应用层
  12. 应用层 HTTP 解析请求头和请求体,如果需要重定向,HTTP 直接返回 HTTP 响应数据的状态 code301 或者 302,同时在请求头的 Location 字段中附上重定向地址,浏览器会根据 code 和 Location 进行重定向操作;如果不是重定向,首先服务器会根据 请求头中的 If-None-Match 的值来判断请求的资源是否被更新,如果没有更新,就返回 304 状态码,相当于告诉浏览器之前的缓存还可以使用,就不返回新数据了;否则,返回新数据,200 的状态码,并且如果想要浏览器缓存数据的话,就在相应头中加入字段: Cache-Control:Max-age=2000 响应数据又顺着应用层——传输层——网络层——网络层——传输层——应用层的顺序返回到网络进程
  13. 数据传输完成,TCP 四次挥手断开连接。如果,浏览器或者服务器在 HTTP 头部加上如下信息,TCP 就一直保持连接。保持 TCP 连接可以省下下次需要建立连接的时间,提示资源加载速度 Connection:Keep-Alive
  14. 网络进程将获取到的数据包进行解析,根据响应头中的 Content-type 来判断响应数据的类型,如果是字节流类型,就将该请求交给下载管理器,该导航流程结束,不再进行;如果是 text/html 类型,就通知浏览器进程获取到文档准备渲染
  15. 浏览器进程获取到通知,根据当前页面 B 是否是从页面 A 打开的并且和页面 A 是否是同一个站点(根域名和协议一样就被认为是同一个站点),如果满足上述条件,就复用之前网页的进程,否则,新创建一个单独的渲染进程
  16. 浏览器会发出“提交文档”的消息给渲染进程,渲染进程收到消息后,会和网络进程建立传输数据的“管道”,文档数据传输完成后,渲染进程会返回“确认提交”的消息给浏览器进程
  17. 浏览器收到“确认提交”的消息后,会更新浏览器的页面状态,包括了安全状态、地址栏的 URL、前进后退的历史状态,并更新 web 页面,此时的 web 页面是空白页
  18. 渲染进程对文档进行页面解析和子资源加载,HTML 通过 HTM 解析器转成 DOM Tree(二叉树类似结构的东西),CSS 按照 CSS 规则和 CSS 解释器转成 CSSOM TREE,两个 tree 结合,形成 render tree(不包含 HTML 的具体元素和元素要画的具体位置),通过 Layout 可以计算出每个元素具体的宽高颜色位置,结合起来,开始绘制,最后显示在屏幕中新页面显示出来

# 浏览器工作流程『从输入 URL 到页面展示』学习笔记

导航

用户输入

  1. 用户在地址栏按下回车,检查输入(关键字 or 符合 URL 规则),组装完整 URL;

    具体流程是用户输入 URL,浏览器会根据用户输入的信息判断是搜索还是网址,如果是搜索内容,就将搜索内容+默认搜索引擎合成新的 URL;如果用户输入的内容符合 URL 规则,浏览器就会根据 URL 协议,在这段内容上加上协议合成合法的 URL

  2. 回车前,当前页面执行 onbeforeunload 事件;

  3. 浏览器进入加载状态。

URL 请求

  1. 浏览器进程通过 IPC 把 URL 请求发送至网络进程;
  2. 查找资源缓存(有效期内);
  3. DNS 解析(查询 DNS 缓存);
  4. 进入 TCP 队列(单个域名 TCP 连接数量限制);
  5. 创建 TCP 连接(三次握手);
  6. HTTPS 建立 TLS 连接(client hello, server hello, pre-master key 生成『对话密钥』);
  7. 发送 HTTP 请求(请求行[方法、URL、协议]、请求头 Cookie 等、请求体 POST);
  8. 接受请求(响应行[协议、状态码、状态消息]、响应头、响应体等);
    • 状态码 301 / 302,根据响应头中的 Location 重定向;
    • 状态码 200,根据响应头中的 Content-Type 决定如何响应(下载文件、加载资源、渲染 HTML)。

准备渲染进程

  1. 根据是否同一站点(相同的协议和根域名),决定是否复用渲染进程。

提交文档

  1. 浏览器进程接受到网路进程的响应头数据,向渲染进程发送『提交文档』消息;
  2. 渲染进程收到『提交文档』消息后,与网络进程建立传输数据『管道』;
  3. 传输完成后,渲染进程返回『确认提交』消息给浏览器进程;
  4. 浏览器接受『确认提交』消息后,移除旧文档、更新界面、地址栏,导航历史状态等;
  5. 此时标识浏览器加载状态的小圆圈,从此前 URL 网络请求时的逆时针选择,即将变成顺时针旋转(进入渲染阶段)。

渲染

渲染流水线

构建 DOM 树

  1. 输入:HTML 文档;
  2. 处理:HTML 解析器解析;
  3. 输出:DOM 数据解构。

样式计算

  1. 输入:CSS 文本;
  2. 处理:属性值标准化,每个节点具体样式(继承、层叠);
  3. 输出:styleSheets(CSSOM)。

布局(DOM 树中元素的计划位置)

  1. DOM & CSSOM 合并成渲染树;
  2. 布局树(DOM 树中的可见元素);
  3. 布局计算。

分层

  1. 特定节点生成专用图层,生成一棵图层树(层叠上下文、Clip,类似 PhotoShop 里的图层);
  2. 拥有层叠上下文属性(明确定位属性、透明属性、CSS 滤镜、z-index 等)的元素会创建单独图层;
  3. 没有图层的 DOM 节点属于父节点图层;
  4. 需要剪裁的地方也会创建图层。

绘制指令

  1. 输入:图层树;
  2. 渲染引擎对图层树中每个图层进行绘制;
  3. 拆分成绘制指令,生成绘制列表,提交到合成线程;
  4. 输出:绘制列表。

分块

  1. 合成线程会将较大、较长的图层(一屏显示不完,大部分不在视口内)划分为图块(tile, 256256, 512512)。

光栅化(栅格化)

  1. 在光栅化线程池中,将视口附近的图块优先生成位图(栅格化执行该操作);
  2. 快速栅格化:GPU 加速,生成位图(GPU 进程)。

合成绘制

  1. 绘制图块命令——DrawQuad,提交给浏览器进程;
  2. 浏览器进程的 viz 组件,根据 DrawQuad 命令,绘制在屏幕上。

相关概念

重排

  1. 更新了元素的几何属性(如宽、高、边距);
  2. 触发重新布局,解析之后的一系列子阶段;
  3. 更新完成的渲染流水线,开销最大。

重绘

  1. 更新元素的绘制属性(元素的颜色、背景色、边框等);
  2. 布局阶段不会执行(无几何位置变换),直接进入绘制阶段。

合成

  1. 直接进入合成阶段(例如 CSS 的 transform 动画);
  2. 直接执行合成阶段,开销最小。

# 总结:

1,用户输入 url 并回车,浏览器进程检查 url,组装协议,构成完整的 url

具体流程是用户输入 URL,浏览器会根据用户输入的信息判断是搜索还是网址,如果是搜索内容,就将搜索内容+默认搜索引擎合成新的 URL;如果用户输入的内容符合 URL 规则,浏览器就会根据 URL 协议,在这段内容上加上协议合成合法的 URL

2,浏览器导航栏显示 loading 状态,但是页面还是呈现前一个页面,这是因为新页面的响应数据还没有获得

3,浏览器进程通过进程间通信(IPC)把 url 请求发送给网络进程

4,网络进程接收到 url 请求后检查本地缓存是否缓存了该请求资源,如果有则将该资源返回给浏览器进程(状态码 200)

5,如果没有,网络进程向 web 服务器发起 http 请求(网络请求),请求流程如下:

  • 5.1 进行 DNS 解析,获取服务器 ip 地址:如果缓存过当前域名信息,就会直接返回缓存信息;
  • 5.2 根据 ip 地址和服务器建立 tcp 连接,tcp 三次握手。
    • 5.2.1 传输层生成 TCP 数据包(包含源端口号、目的端口号),继续向下传输到网络层
    • 5.2.2 给数据包加上 IP 头部——包括源 IP、目的 IP,继续向下传输到物理层
    • 5.2.3 物理层通过物理网络(如光纤)传输给目的服务器主机
    • 5.2.4 目的服务器网络层接收到数据包,解析出 IP 头部,识别出数据部分,将解开的数据包向上传输到传输层
    • 5.2.5 目的服务器传输层获取到数据包,解析出 TCP 头部,识别端口
  • 5.3 如果是 https 请求则还需要建立 TLS 连接。
  • 5.4 构建请求头信息
  • 5.5 发送请求头信息
  • 5.6 服务器响应后,网络进程接收响应头和响应信息,并解析响应内容
  • 5.7 数据传输完成,TCP 四次挥手断开连接。如果在 HTTP 头部加上 Connection:Keep-Alive,TCP 就一直保持连接。保持 TCP 连接可以省下下次需要建立连接的时间,提示资源加载速度

6,网络进程解析响应流程;

  • 6.1 检查状态码,如果是 301/302,则需要重定向,从 Location 自动中读取地址,重新进行第 4 步,
  • 6.2 如果不是重定向,首先服务器会根据 请求头中的 If-None-Match 的值来判断请求的资源是否被更新, 如果没有更新,就返回 304 状态码,相当于告诉浏览器之前的缓存还可以使用,就不返回新数据了
  • 6.3 200 响应处理: 检查响应类型 Content-Type。 如果是 字节流 类型,则将该请求提交给下载管理器,该导航流程结束,不再进行后续的渲染。 如果是 text/html 则通知浏览器进程,准备渲染进程进行渲染。

7,准备渲染进程

  • 7.1 浏览器进程获取到通知,根据当前页面 B 是否是从页面 A 打开的并且和页面 A 是否是同一个站点(根域名和协议一样就被认为是同一个站点)。
  • 7.2 如果相同,则复用原来的进程。如果不同,新创建一个新的渲染进程
  1. 传输数据、更新状态
  • 8.1 渲染进程准备好后,浏览器进程向渲染进程发起“提交文档”的消息,渲染进程接收到消息后,和网络进程建立传输数据的“管道”
  • 8.2 渲染进程接收完数据后,向浏览器发送“确认提交”
  • 8.3 浏览器进程接收到“确认提交”的消息后,更新浏览器界面状态:包括安全状态、地址栏的 URL、前进后退的历史状态,并更新 web 页面,此时的 web 页面是空白页。
  1. 渲染进程渲染页面
  • 9.1 构建 DOM 树
    • 9.1.1 输入:HTML 文档 -> 处理:HTML 解析器解析 -> 输出:DOM 数据解构
    • 9.1.2 具体:接收到 Bytes 字节流->(转换)HTML 字符串->(解析)Token->(构建)Node->(合并)Dom 树
  • 9.2 样式计算(构建 CSSOM 树)
    • 9.2.1 输入:CSS 文本(Link 外部引入、style 标签、元素 style 属性)
    • 9.2.2 处理:属性值标准化,每个节点具体样式(继承、层叠)
    • 9.2.3 输出:styleSheets(CSSOM 树)
  • 9.3 页面布局(构建布局树)
    • 9.3.1 作用是:计算出 DOM 树中可见元素的几何位置
    • 9.3.2 具体:先将 DOM 树和 CSSOM 树合并为布局树(只包含可见节点),然后进行布局计算,计算布局树节点的坐标位置。
  • 9.4 分层
    • 9.4.1 特定节点生成专用图层,生成一棵图层树(层叠上下文、Clip,类似 PhotoShop 里的图层);
    • 9.4.2 拥有层叠上下文属性(明确定位属性、透明属性、CSS 滤镜、z-index 等)的元素会创建单独图层;
    • 9.4.3 没有图层的 DOM 节点属于父节点图层;
    • 9.4.4 需要剪裁的地方也会创建图层。
  • 9.5 图层绘制
    • 9.5.1 输入:图层树;
    • 9.5.2 渲染引擎对图层树中每个图层进行绘制;
    • 9.5.3 拆分成绘制指令,生成绘制列表,提交到合成线程;
    • 9.5.4 输出:绘制列表。
  • 9.6 分块(将图层划分为图块) 合成线程会将较大、较长的图层(一屏显示不完,大部分不在视口内)划分为图块(tile, 256*256, 512*512)。
  • 9.7 栅格化(栅格化,是指将图块转换为位图)
    • 9.7.1 在栅格化线程池中,将视口附近的图块优先生成位图(栅格化执行该操作);
    • 9.7.2 快速栅格化:GPU 加速,生成位图(GPU 进程)。
  • 9.8 合成和显示
    • 9.8.1 绘制图块命令——DrawQuad,提交给浏览器进程;
    • 9.8.2 浏览器进程的 viz 组件,接收合成线程发过来的 DrawQuad 命令,根据 DrawQuad 命令,绘制在屏幕上。

# 最后

文中若有不准确或错误的地方,欢迎指出,有兴趣可以的关注下Github~

Last Updated: 2020/9/7 下午8:45:37