说一说从 URL 输入到页面呈现到底发生了什么?

前言

这是面试过程中一道高频考题。

从面试官的角度思考:

  • 出现频繁,可能是因为面试官通常喜欢问一些考察可深可浅的题目
  • 很多面试管喜欢根据我们应聘者的考题回答中,甚至我们随口说到的知识点,继续追问

基本回答:

  • 浏览器解析 URL 获取协议,主机,端口, path
  • 浏览器获取主机 ip 地址
  • 建立 TCP 连接,然后发送 HTTP 请求
  • 服务器将响应报文通过 TCP 连接发送回浏览器,浏览器接收 HTTP 响应,根据资源类型决定如何处理(假设资源为 HTML 文档)
  • 解析 HTML 文档,构件 DOM 树,下载资源,构造 CSSOM 树,执行 js 脚本,最后展现出来给用户

如果应聘者只回答了上述步骤,很多关键步骤(前端应该了解的知识点)没有提及,很有可能达不到面试官想要的回答效果。

笔者针对一些关键步骤,具体展开说明。让这道题成为我们面试考卷中的加分项

网络请求

构建请求

浏览器会构建请求行:

// 请求方法是 GET,路径为根路径,HTTP 协议版本为 1.1
GET / HTTP/1.1

然后根据 Cache-control 和 Expires 字段,检查强缓存,如果命中直接使用,否则进入下一步。关于强缓存,如果不清楚可以参考下图:

DNS 解析

由于我们输入的是域名,而数据包是通过IP地址传给对方的。因此我们需要得到域名对应的IP地址。这个过程需要依赖一个服务系统,这个系统将域名和 IP 一一映射,我们将这个系统就叫做DNS(域名系统)。

DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务。得到具体 IP 的过程就是DNS解析。

DNS 是一个网络服务器,我们的域名解析简单来说就是在 DNS 上记录一条信息记录。

例如 baidu.com 220.114.23.56(服务器外网IP地址)80(服务器端口号)

浏览器通过域名去查询 URL 对应的 IP :

  • 浏览器缓存:浏览器会按照一定的频率缓存 DNS 记录
  • 操作系统缓存:如果浏览器缓存中找不到需要的 DNS 记录,那就去操作系统中找
  • 路由缓存:路由器也有 DNS 缓存
  • ISP 的 DNS 服务器:ISP 是互联网服务提供商( Internet Service Provider )的简称,ISP 有专门的 DNS 服务器应对 DNS 查询请求
  • 根服务器:ISP 的 DNS 服务器还找不到的话,它就会向根服务器发出请求,进行递归查询(DNS 服务器先问根域名服务器 .com 域名服务器的 IP 地址,然后再问 .baidu 域名服务器,依次类推)

建立 TCP 连接

TCP 三次握手的过程如下:

  • 客户端发送一个带 SYN=1,Seq=X 的数据包到服务器端口(第一次握手,由浏览器发起,告诉服务器我要发送请求了)
  • 服务器发回一个带 SYN=1, ACK=X+1, Seq=Y 的响应包以示传达确认信息(第二次握手,由服务器发起,告诉浏览器我准备接受了,你赶紧发送吧)
  • 客户端再回传一个带 ACK=Y+1, Seq=Z 的数据包,代表“握手结束”(第三次握手,由浏览器发送,告诉服务器,我马上就发了,准备接受吧)

谢希仁著《计算机网络》中讲“三次握手”的目的是“为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误”。

发送 HTTP 请求

现在 TCP 连接建立完毕,浏览器可以和服务器开始通信,即开始发送 HTTP 请求。浏览器发 HTTP 请求要携带三样东西:请求行请求头请求体

[图片上传失败...(image-f9c53-1587981019451)]

1.请求行包含请求方法、URL、协议版本

  • 请求方法包含 8 种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE
  • URL 即请求地址,由 <协议>://<主机>:<端口>/<路径>?<参数> 组成
  • 协议版本即 http 版本号
POST /user.html HTTP/1.1

2.请求头包含请求的附加信息,由关键字/值对组成,如下

// 服务器可以接受的文件格式
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng;q=0.8,application/signed-exchange;v=b3
// 指定浏览器可以支持的 Web 服务器返回的内容压缩编码类型
Accept-Encoding: gzip, deflate, br
// 浏览器支持的语言
Accept-Language: zh-CN,zh;q=0.9
// 缓存机制
Cache-Control: no-cache
// 是否需要持久连接
Connection: keep-alive
// 发送该请求域名下所有 Cookie 值到服务器
Cookie: /* 省略cookie信息 */
// 指定请求的服务器的域名和端口号
Host: www.baidu.com
Pragma: no-cache
Upgrade-Insecure-Requests: 1
// 用户代理 UA,包含发出请求的用户信息
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1

3.请求体,可以承载多个请求参数的数据,包含回车符、换行符和请求数据,一般在 POST 方法下存在。

网络响应

跟请求部分类似,网络响应具有三个部分:响应行响应头响应体

1.响应行包含:协议版本,状态码,状态码描述

HTTP/1.1 200 OK

状态码规则如下:

  • 1xx:指示信息--表示请求已接收,继续处理
  • 2xx:成功--表示请求已被成功接收、理解、接受
  • 3xx:重定向--要完成请求必须进行更进一步的操作
  • 4xx:客户端错误--请求有语法错误或请求无法实现
  • 5xx:服务器端错误--服务器未能实现合法的请求

2.响应头部包含响应报文的附加信息,由 名/值 对组成,如下:

// 缓存机制
Cache-Control: no-cache
Connection: keep-alive
Content-Encoding: gzip
// 表示具体请求中的媒体类型信息,决定浏览器将以什么形式、什么编码读取这个文件
Content-Type: text/html;charset=utf-8
// 原始服务器消息发出的时间
Date: Wed, 04 Dec 2019 12:29:13 GMT
// Web 服务器软件名称
Server: apache
// 由服务器端向客户端发送 cookie
Set-Cookie: rsv_i=f9a0SIItKqzv7kqgAAgphbGyRts3RwTg%2FLyU3Y5Eh5LwyfOOrAsvdezbay0QqkDqFZ0DfQXby4wXKT8Au8O7ZT9UuMsBq2k; path=/; domain=.baidu.com

这里注意下 Set-Cookie 中关于网络安全方面的两个值:HttpOnly、SameSite

设置了 HttpOnly 属性的 cookie 不能使用 JavaScript 经由 Document.cookie 属性、XMLHttpRequest 和 Request APIs 进行访问,以防范跨站脚本攻击(XSS)。

SameSite=Lax 允许服务器设定一则 cookie 不随着跨域请求一起发送,这样可以在一定程度上防范跨站请求伪造攻击(CSRF)。

响应完成之后要判断 Connection 字段,如果请求头或响应头中包含 Connection: Keep-Alive ,表示建立了持久连接,这样 TCP 连接会一直保持,之后请求统一站点的资源会复用这个连接。
否则断开 TCP 连接, 请求-响应流程结束。

3.响应主体包含回车符、换行符和响应返回数据,并不是所有响应报文都有响应数据

总结浏览器端的网络请求过程:

image

浏览器解析渲染页面

image

浏览器解析渲染页面分为以下五个步骤:

  • 根据 HTML 解析出 DOM 树
  • 根据 CSS 解析生成 CSS 规则树
  • 结合 DOM 树和 CSS 规则树,生成渲染树
  • 根据渲染树计算每一个节点的信息
  • 根据计算好的信息绘制页面

回流时,以上流程会重新走一遍。重绘时,会重新计算样式,跳过中间步骤直接生成绘制列表。可见,重绘不一定导致回流,但回流一定发生了重绘。

image

构建 DOM 树

  • HTML语法定义

    HTML的词汇与句法定义在w3c组织创建的规范中。当前版本是HTML4,HTML5的工作正在进行中。

  • 不是上下文无关语法

    在对解析器的介绍中看到,语法可以用类似 BNF 的格式规范地定义。不幸的是所有常规解析器的讨论都不适用于 HTML (我提及它们并不是为了娱乐,它们可以用于解析 CSS 和 JavaScript )。HTML 无法用解析器所需的上下文无关的语法来定义。过去 HTML 格式规范由 DTD (Document Type Definition) 来定义,但它不是一个上下文无关语法。

    HTML 与 XML 相当接近。XML 有许多可用的解析器。HTML 还有一个 XML 变种叫 XHTML ,那么它们主要区别在哪里呢?区别在于 HTML 应用更加”宽容”,它容许你漏掉一些开始或结束标签等。它整个是一个“软”句法,不像 XML 那样严格死板。 总的来说这一看似细微的差别造成了两个不同的世界。一方面这使得 HTML 很流行,因为它包容你的错误,使网页作者的生活变得轻松。另一方面,它使编写语法格式变得困难。所以综合来说,HTML 解析并不简单,现成的上下文相关解析器搞不定,XML 解析器也不行。

  • 解析算法

    • 标记化
    • 建树

    对应的两个过程就是分词和语法分析(参考Babel 编译的解析过程)。

    这里举例重点介绍下 HTML5容错机制

    • 使用 </br> 而不是 <br>

      if (t->isCloseTag(brTag) && m_document->inCompatMode()) {
          reportError(MalformedBRError);
          t->beginTag = true;
      }
      

      全部换为 <br> 的形式。

    • 表格离散

      <table>
          <table>
              <tr><td>inner table</td></tr>
          </table>
          <tr><td>outer table</td></tr>
      </table>
      

      WebKit 会自动转换为:

      <table>
          <tr><td>outer table</td></tr>
      </table>
      <table>
          <tr><td>inner table</td></tr>
      </table>
      
    • 表单元素嵌套

      这时候直接忽略里面的 form

样式计算

CSS 样式来源一般为三种:

  • link 标签引用
  • style 标签中样式
  • 元素内嵌 style 属性
格式化样式表

浏览器无法直接识别 CSS 样式文本,这里渲染引擎接收到 CSS 文本之后将其转化为一个结构话的对象,即 styleSheets 。

可以在浏览器控制台输入 document.styleSheets 来查看这个最终结构(包含上述三种 CSS 来源)。

标准化样式属性

有一些渲染引擎不容易直接理解的 CSS 样式数值,需要在计算样式之前将它们标准化。如:em -> pxred -> #ff0000bold -> 700 等等。

计算每个节点的具体样式

计算具体样式主要遵循两个规则:继承层叠

  • 继承:

    每个子节点都会默认继承父节点的样式属性,如果父节点中没有找到,就采用浏览器默认样式,也叫 UserAgent样式

  • 层叠:

    CSS 的层叠性体现在,最终的样式取决与各个属性共同作用的结果。

计算完样式之后,所有样式值会被挂载到 window.getComputedStyle 中,也就是可以通过 JS 获取计算后的样式。

生成布局树

布局树生成主要分两部:

  • 遍历生成的 DOM 树节点,并把它们添加到布局树中
  • 计算布局树节点的坐标位置

布局树只包含可见元素,对于 head 标签和设置了 display: none 的元素将不会被放入其中。

如果想了解布局的细节,可以读一读人人 FED 团队的文章从Chrome源码看浏览器如何layout布局

构建图层树

这里分两种情况,一种是显式合成,一种是隐式合成

显式合成

一、拥有层叠上下文的节点

层叠上下文也基本上是有一些特定的 CSS 属性创建的,一般有以下情况:

  1. HTML 根元素本身就具有层叠上下文

  2. 普通元素设置 position 不为 static 并且设置了 z-index 属性,会产生层叠上下文

  3. 元素的 opacity 值不是 1

  4. 元素的 transform 值不是 none

  5. 元素的 filter 值不是 none

  6. 元素的 isolation 值是 isolate

  7. will-change 指定的属性值为上面任意一个

二、需要剪裁的地方

比如一个 div,你只给他设置 100 * 100 像素的大小,而你在里面放了非常多的文字,那么超出的文字部分就需要被剪裁。当然如果出现了滚动条,那么滚动条会被单独提升为一个图层。

隐式合成

简单说就是层叠等级低的节点被提升为单独的图层之后,那么所有层叠等级比它高的节点都会成为一个单独的图层。

这个隐式合成其实隐藏着巨大风险,如果在一个大型应用中,当一个 z-index 比较低的元素被提升为单独图层之后,层叠在它上面的元素统统会被提升为单独的图层,可能会增加上千个图层,大大增加内存压力,甚至直接让页面崩溃。这就是层爆炸的原理

当需要 repaint 时,只需要 repaint 本身,而不会影响到其他层。

生成绘制列表

渲染引擎会将图层的绘制拆分成一个个绘制指令,比如先画背景、再描绘边框......然后将这些指令按顺序组合成一个待绘制列表。

大家可以 F12 打开 Chrome 开发者工具,在设置栏展开 more tools ,然后选择 Layers 面板,就能看到绘制列表了。

后面就是渲染进程的主线程把绘制列表提交给合成线程。然后合成线程选择视口附近的图块,把它交给栅格化线程池生成位图。

栅格化操作完成后,合成线程会生成一个绘制指令 DrawQuad,并发送给浏览器进程。浏览器进程中的 viz 组件 接收到命令,把页面内容绘制到内存,也就是生成了页面。

断开连接

当数据传送完毕,需要断开 TCP 连接,此时发起四次挥手。

image
  • 发起方往被动方发送报文,Fin、Ack、Seq,表示已经没有数据传输了。并进入 FIN_WAIT_1 状态。(请求报文发送完成)
  • 被动方发送报文,Ack、Seq,表示同意关闭请求。此时主机发起方进入 FIN_WAIT_2 状态。(请求报文接受完成)
  • 被动方向发起方发送报文段,Fin、Ack、Seq,请求关闭连接。并进入 LAST_ACK 状态。(响应报文发送完成)
  • 发起方向被动方发送报文段,Ack、Seq。然后进入等待 TIME_WAIT 状态。被动方收到发起方的报文段以后关闭连接。发起方等待一定时间未收到回复,则正常关闭。(响应报文接受完成)

参考文章

感谢

如果本文对你有帮助,就点个赞支持下吧!感谢阅读。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容