跳到主要内容

请求

输入

当在地址栏输入一个查询关键字的时候,地址栏会判断,输入的是需要搜索的关键字,还是请求的 URL 。如果是关键字,地址栏会结合浏览器所设置的搜索引擎来生成一个带输入关键字的 URL , 然后开始发起 URL 请求。如果浏览器判断输入的是合法的 URL ,那么浏览器也会进行相应的修正,比如为 lmssee.com 加上协议,合成完整的 URL : https://lmssee.com 然后开始请求。

当用户输入关键字并键入回车之后,这意味着当前页面即将要被替换成新的页面,不过在这个流程继续之前,浏览器还给了当前页面一次执行 beforeunload 事件的机会, beforeunload 事件允许页面在退出之前执行一些数据清理操作,还可以询问用户是否要离开当前页面,比如当前页面可能有未提交完成的表单等情况,因此用户可以通过 beforeunload 事件来取消导航,让浏览器不再执行任何后续工作。

URL 请求过程

此时,开始进入页面资源请求过程。浏览器会进行进程间通信 (IPC) 把 URL 请求发送到网络进程。当网络进程接收到 URL 请求时,首先网络进程会查找本地是否具有缓存。如果具备缓存资源,那么直接返回浏览器该缓存资源,否则直接进入网络请求流程。

请求的第一步会进入 DNS 解析,来获取域名的 IP 地址。域名解析需要访问一系列的域名解析服务器,来把该域名翻译成为 TCP/IP 协议的 IP 地址。

为提高 performance ,域名解析的过程中会设置多级的缓存,浏览器首先看一下自己的缓存里有没有,如果没有就向操作系统的缓存要,还没有就检查本机域名解析文件 hosts 。如果本地都找不到缓存,就会去顶级域名服务器“ com ”去查找,并且将该结果缓存。

当 Client 利用 IP 地址和服务器建立 TCP 连接后,浏览器会构建请求行、请求头等信息,并将该域名相关的 Cookie 等数据附加到请求头中,向服务器发送请求信息。 服务器接收到请求信息后,会根据请求信息生成响应数据(包括响应行、响应头和响应体等信息),并发给网络进程。等网络进程接收了响应行和响应头之后,就开始解析响应的内容了。

重定向

在接收到服务器返回的响应头后,网络进程开始解析响应头,如果发现返回的状态码是 301 或者 302 ,那么说明服务器需要浏览器重定向到其他 URL 。这时网络进程会从响应头的 Location 字段里面读取重定向的地址,然后再发起新的 HTTP 或者 HTTPS 请求,重头开始。如果响应行是 200 ,那么表示浏览器可以继续处理该请求。

应数据类型处理

通常 URL 请求的数据类型会包含 HTML 页面,或者下载数据类型。响应头中的 Content-type 会告知浏览器返回的数据类型。如 Content-type : text/html 表示返回数据 类型为 HTML 格式, Content-Type : application/octet-stream 表示返回数据类型为字节流类型,浏览器按照下载类型来处理。不同的 Content-Type 字段,浏览器后续处理流程会有所不同。如果是下载类型,那么浏览器会将请求任务提交给下载管理器,同时关闭导航流程。如果是 HTML 浏览器的导航流程还会继续,并通知渲染进程开始进行渲染页面。

准备渲染进程

默认情况下, Chrome 会为每个页面分配一个渲染进程,也就是说,每打开一个新页面就会配套创建一个新的渲染进程。但是,也有一些例外,在某些情况下,浏览器会让多个页面直接运行在同一个渲染进程中。

当从新的页面打开另外一个页面,并且这些页面属于同一个站点 (same-site) 的话 , 那么新页面会复用父页面的渲染进程 (process-per-site-instance) 。

提交文档

渲染进程准备好之后,它就会通知浏览器进程,可以替换当前旧的文档了,具体地讲,需要经过下列几个步骤 :

  • 首先“提交文档”的消息是由渲染进程发出给浏览器进程的,这是告诉浏览器进程,它已经准备好了,可以执行解析渲染等后续操作了浏览器进程接收到当前渲染进程的“提交文档”消息后,便开始清理当前的旧文档,然后会发出“确认提交”的消息给渲染进程。同时,浏览器进程会更新浏览器界面状态,包括了安全状态、地址栏的 URL 、前进后退的历史状态,并更新 Web 页面
  • 当渲染进程接收到“确认提交”的消息后,便开始执行解析数据、下载子资源等后续流程,并实时向浏览器进程更新最新的渲染状态

渲染阶段

文档被提交,渲染进程便开始页面解析和子资源加载了。该过程主要涉及 HTML,CSS,JavaScript 如何生成页面。从解析 *.html 文件,生成 DOM, CssDOM, Render Tree, 再到计算布局 (layout) ,绘制 (paint) ,合成页面 (composite layers), 每个过程都涉及到优化问题。开发者应该注重用户体验,防止页面卡顿,使用 JavaScript 优化动画流程,优化样式表等来防止强制同步布局等。

生成 Render Tree

然后根据 CSS 的样式规则,我们依次得到各 Node 与对应的 CSS 样式结合,再生成 Render Tree 。其中 Render Tree 与 DOM tree 非常类似,不同的是 Render tree 没有 head 也不含任何脚本。如果 CSS 样式中设置了 {display: none; } 属性的话,该 node 也会从 Render tree 中移掉,对于一些伪元素,例如添加了

h1:after {
content: 'this will be visible';
}

属性, Render tree 会添加该 node, 但 DOM 并不会包含这个 node 。需要注意的是,只有实际显示在网页上的元素才会进入 Render tree 。

计算布局

当浏览器知道哪个规则适用于哪个元素后,就会开始进入布局计算阶段。其中具体样式适用元素的规则需要根据 CSS 的继承规则和层叠规则计算。我们可以通过盒模型,排版等计算每个节点坐标,大小,得到页面的布局。这一过程比较复杂,该计算过程也称为回流 (reflow) 。

光栅化

得到计算的布局之后,便可以矢量光栅化到图形 (vector 2 raster) ,矢量的栅格化到图形 render tree 通过计算边框、绘制文本、绘制阴影、白线、绘制位图最终形成的展示内容。

合成页面

浏览器会根据需要创建多个图层,单独绘制这些图层并合成层。

对于绘制和合成页面的中间过程,还会涉及到跨进程操作。通常,绘制过程中,都是在一系列的网格图块( tiles )中实现的。图层会将其本身以及图块上传到 GPU 中进行栅格化加速生成。使用 GPU 生成位图的过程叫快速栅格化,或者 GPU 栅格化,生成的位图被保存在 GPU 内存中。 GPU 操作是运行在 GPU 进程中,如果栅格化操作使用了 GPU ,那么最终生成位图的操作是在 GPU 中完成的。

123

HTTP 请求过程

  • 浏览器从地址栏的输入中获得服务器的 IP 地址和端口号
  • 浏览器用 TCP 的三次握手与服务器建立连接
  • 浏览器向服务器发送拼好的报文
  • 服务器收到报文后处理请求,同样拼好报文再发给浏览器;浏览器解析报文,渲染输出页面

页面渲染过程

  • 根据 HTML 构建 DOM 树
  • 根据 CSS 样式表,计算 DOM 树所有节点样式并构建 Render tree 。
  • 计算 Layout 布局
  • 根据 Layout 进行分层
  • 各图层分成图块,并在 GPU 中转换图块成位图
  • 生成页面,并显示到显示器

交互

首次载入网站进行访问时,浏览器和服务器的完整交互步骤如下。

  • 在浏览器中请求访问网站 https://lmssee.cn/about
  • 服务器返回 index.html 页面
  • 浏览器解析 index.html 页面,请求页面中的所有资源文件,包括 HTML 文件、 CSS 文件、 JavaScript 脚本文件、清单文件以及图片等
  • 服务器端返回所有请求的资源文件
  • 在浏览器中处理并请求清单中的所有项,包括 index.html 页面本身。如果页面内容过多,并且要求本地缓存所有的文件,这将是一个比较的大重复的请求过程
  • 服务器返回所有要求本地缓存的文件
  • 浏览器对本地缓存进行更新,存入包括页面本身在内的所有要求本地缓存的资源文件,并且触发一个事件,通知本地缓存被更新

再次载入页面

首先访问网站首页完毕后,浏览器将清单中的列表文件完全载入缓存。如果再次打开浏览器访问该网站,并且 manifest 文件中的内容没有被修改过,它们的交互过程如下 。

  • 浏览器会再次请求访问网站 https://lmssee.cn/about
  • 浏览器会发现这个页面被本地缓存,于是使用本地缓存中的 clock.html 页面
  • 浏览器解析 clock.html 网页,使用所有本地缓存中的资源文件
  • 浏览器向服务器请求 manifest 文件
  • 服务器返回一个 304 代码,通知浏览器 manifest 文件没有发生变化

如果再次通过浏览器访问网站页面,并且 manifest 文件的内容已经被修改过了,它们的交互过程如下

  • 浏览器会再次请求访问 https://lmssee.cn/about
  • 浏览器会发现这个页面被本地缓存,于是使用本地缓存中的 index.html 页面
  • 浏览器解析 index.html 网页,使用所有本地缓存中的资源文件
  • 浏览器向服务器请求 manifest 文件
  • 服务器返回更新过的 manifest 文件
  • 浏览器会处理 manifest 文件,发现该文件已经被更新,于是请求所有要求本地缓存的资源文件,包含 index.html 页面本身
  • 浏览器返回要求进行本地缓存的资源文件
  • 浏览器对本地缓存进行更新,存入所有新的资源文件,并且触发一个事件,通知本地缓存被更新

即使资源文件被修改过了,任何之前载入的资源都不会发生变化。例如,图片不会改变,旧的 JavaScript 函数也不会改变。也就是说,更新过后的本地缓存中的内容还不能被使用,只有重新打开这个页面的时候才会使用更新过的资源文件