[TOC]

0x00 前言介绍

Web的发展历程

  • Web 1.0 用户访问的页面是没有权限更改的,只是一个个单单的静态页面。

    WeiyiGeek.

    WeiyiGeek.

  • Web 2.0 则更注重用户的交互作用,用户既是网站内容的浏览者,也是网站内容的制造者

    1
    2
    3
    4
    5
    * 用户参与网站内容的制造
    * Web2.0更加注重交互性
    * 符合Web标准的网站设计
    * Web2.0网站与Web1.0没有绝对的界限
    * Web2.0的核心不是技术,而在于思想
  • Web3.0 只是由业内人员制造出来的概念词语,最常见的解释是,网站内的信息可以直接和其他网站相关信息进行交互,能通过第三方信息平台同时对多家网站的信息进行整合使用;
    用户在互联网上拥有自己的数据,并能在不同网站上使用;完全基于web,用浏览器即可实现复杂系统程序才能实现的系统功能,用户数据审计后,同步于网络数据。

    • 例如:QQ的直接登录其他接入QQAPI的便能直接使用QQ登录并建立账号。
      WeiyiGeek.

      WeiyiGeek.


0x01 语言代码标准

ISO 语言代码
描述:HTML 的 lang 属性可用于网页或部分网页的语言。这对搜索引擎和浏览器是有帮助的。为各种语言定义了缩略词,您可以在 HTML 和 XHTML 中的 lang 和 xml:lang 属性中使用它们。

常用语言代码:

1
2
3
4
* Language	ISO Code
* Chinese (Simplified) zh
* Chinese (Traditional) zh
* English en


使用方式:
根据 W3C 推荐标准,您应该通过 标签中的 lang 属性对每张页面中的主要语言进行声明,比如:

1
2
3
<html lang="en">
...
</html>

在 XHTML 中,采用如下方式在 标签中对语言进行声明:

1
2
3
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
...
</html>


0x02 浏览器解析流程

浏览器(采用BS简称)实现一次HTTP请求连接大体上,简单可以分为六步:

  • (1)合成URL:浏览器分析指向页面的URL
  • (2)DNS域名解析:浏览器向DNS请求解析出域名的IP地址
  • (3)建立TCP连接:浏览器与服务器建立TCP连接
  • (4)发出HTTP请求:浏览器发出请求取文件命令给服务器
  • (5)服务器给出响应,将文件发给浏览器
  • (6)TCP连接释放 : 关闭TCP连接四次挥手;
  • (7)浏览器显示文件中的所有文本 (该阶段是本文的重点)
WeiyiGeek.流程详细

WeiyiGeek.流程详细

详细说明:
1.DNS域名解析: 类比邮政编码,你可以采用邮政编码快速找到您需要的地址;DNS 服务器是高可用、高并发和分布式的,它是树状结构,

  • 根 DNS 服务器 :返回顶级域 DNS 服务器的 IP 地址
  • 顶级域 DNS 服务器:返回权威 DNS 服务器的 IP 地址
  • 权威 DNS 服务器 :返回相应主机的 IP 地址
    DNS的域名查找在客户端和浏览器,本地DNS之间的查询方式是递归查询;在本地DNS服务器与根域及其子域之间的查询方式是迭代查询;如果本地DNS服务器无法查询到,则根据本地DNS服务器设置的转发器进行查询。
WeiyiGeek.结合起来的过程

WeiyiGeek.结合起来的过程

在查找过程中,有以下优化点:

  • DNS存在着多级缓存,从离浏览器的距离排序的话,有以下几种: 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存
  • 在域名和 IP 的映射过程中,给了应用基于域名做负载均衡的机会,可以是简单的负载均衡,也可以根据地址和运营商做全局的负载均衡。

2.建立TCP连接:首先判断是不是https的,如果是则HTTPS其实是HTTP + SSL / TLS 两部分组成,也就是在HTTP上又加了一层处理加密信息的模块。

  • 服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后的数据。
  • 进行三次握手,建立TCP连接。(参考其他的文章TCP3次握手:ACK / SYN / FIN
  • SSL握手过程
    • 第一阶段:建立安全能力 包括SSL协议版本 会话Id 密码构件 压缩方法和初始随机数
    • 第二阶段:服务器发送证书 密钥交换数据和证书请求,最后发送请求-相应阶段的结束信号
    • 第三阶段:如果有证书请求客户端发送此证书 之后客户端发送密钥交换数据 也可以发送证书验证消息
    • 第四阶段:变更密码构件和结束握手协议

备注:

  • ACK:此标志表示应答域有效,就是说前面所说的TCP应答号将会包含在TCP数据包中;有两个取值:0和1,为1的时候表示应答域有效,反之为0。TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1。
  • SYN(SYNchronization):在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1. 因此, SYN置1就表示这是一个连接请求或连接接受报文。
  • FIN(finis)即完,终结的意思, 用来释放一个连接。当 FIN = 1 时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。

3.发送请求与处理:TCP连接建立后,浏览器就可以利用HTTP/HTTPS协议向服务器发送请求了;服务器接受到请求,就解析请求头,如果头部有缓存相关信息如if-none-match与if-modified-since,则验证缓存是否有效,若有效则返回状态码为304,若无效则重新返回资源,状态码为200.

  • HTTP缓存大致过程:
    WeiyiGeek.

    WeiyiGeek.

4.关闭TCP连接:详细的参考TCP协议

  • 第一次分手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number和Acknowledgment Number,向主机2发送一个FIN报文段;此时,主机1进入FIN_WAIT_1状态;这表示主机1没有数据要发送给主机2了;

  • 第二次分手:主机2收到了主机1发送的FIN报文段,向主机1回一个ACK报文段,Acknowledgment Number为Sequence Number加1;主机1进入FIN_WAIT_2状态;主机2告诉主机1,我”同意”你的关闭请求;

  • 第三次分手:主机2向主机1发送FIN报文段,请求关闭连接,同时主机2进入LAST_ACK状态;

  • 第四次分手:主机1收到主机2发送的FIN报文段,向主机2发送ACK报文段,然后主机1进入TIME_WAIT状态;主机2收到主机1的ACK报文段以后,就关闭连接;此时主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。


敲黑板重点来了:


5.浏览器渲染
浏览器渲染过程(按时间顺序):构建 DOM 树、CSS样式计算、布局阶段、分层、栅格化和显示

  • 1.构建 DOM 树:渲染进程将 HTML 内容转换为能够读懂DOM 树结构。
    • 浏览器从网络或硬盘中获得HTML字节数据后会经过一个流程将字节解析为DOM树,先将HTML的原始字节数据转换为文件指定编码的字符,然后浏览器会根据HTML规范来将字符串转换成各种令牌标签,如html、body等。最终解析成一个树状的对象模型,就是dom树。
      1
      2
      3
      4
      5
      #具体步骤:
      1.转码(Bytes -> Characters)—— 读取接收到的 HTML 二进制数据,按指定编码格式将字节转换为 HTML 字符串
      2.Tokens 化(Characters -> Tokens)—— 解析 HTML,将 HTML 字符串转换为结构清晰的 Tokens,每个 Token 都有特殊的含义同时有自己的一套规则
      3.构建 Nodes(Tokens -> Nodes)—— 每个 Node 都添加特定的属性(或属性访问器),通过指针能够确定 Node 的父、子、兄弟关系和所属 treeScope(例如:iframe 的 treeScope 与外层页面的 treeScope 不同)
      4.构建 DOM 树(Nodes -> DOM Tree)—— 最重要的工作是建立起每个结点的父子兄弟关系
WeiyiGeek.

WeiyiGeek.

  • 2.CSS样式计算:渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets并且计算出 DOM 节点的样式
    • CSS 样式来源主要有 3 种,分别是通过 link 引用的外部 CSS 文件、style标签内的 CSS、元素的 style 属性内嵌的 CSS
    • 如下所示 CSS 文本中有很多属性值,如 2em、blue、bold,这些类型数值不容易被渲染引擎理解,所以需要将所有值转换为渲染引擎容易理解的、标准化的计算值,这个过程就是属性值标准化。处理完成后再处理样式的继承和层叠,有些文章将这个过程称为CSSOM的构建过程。
WeiyiGeek.

WeiyiGeek.

  • 3.布局阶段:创建布局树并计算元素的布局信息。
    • 排除 script、meta 等功能化、非视觉节点,排除 display: none 的节点,计算元素的位置信息,确定元素的位置,构建一棵只包含可见元素布局树;
    • 布局完成过程中(如果有js操作或者其他操作,对元素的颜色,背景等作出改变就会引起重绘),(如果有对元素的大小、定位等有改变则会引起回流
      WeiyiGeek.

      WeiyiGeek.

  • 4.分层:对布局树进行分层并生成分层树。

    • 页面中有很多复杂的效果,如一些复杂的 3D 变换、页面滚动,或者使用 z-indexing 做 z 轴排序等,为了更加方便地实现这些效果,渲染引擎还需要为特定的节点生成专用的图层并生成一棵对应的图层树(LayerTree);如果你熟悉 PS相信你会很容易理解图层的概念,正是这些图层叠加在一起构成了最终的页面图像
    • 在浏览器中,你可以打开 Chrome 的”开发者工具”,选择”Layers”标签。渲染引擎给页面分了很多图层,这些图层按照一定顺序叠加在一起,就形成了最终的页面。
    • 注意事项:并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层
  • 5.栅格化:为每个图层生成绘制列表,并将其提交到合成线程;合成线程将图层分图块,并栅格化将图块转换成位图( 所谓栅格化是指将图块转换为位图)。

    • 通常一个页面可能很大,但是用户只能看到其中的一部分,我们把用户可以看到的这个部分叫做视口(viewport)。
    • 在有些情况下,有的图层可以很大,比如有的页面你使用滚动条要滚动好久才能滚动到底部,但是通过视口,用户只能看到页面的很小一部分,所以在这种情况下要绘制出所有图层内容的话,就会产生太大的开销,而且也没有必要。
      WeiyiGeek.

      WeiyiGeek.

  • 6.浏览显示:合成线程发送绘制图块命令给浏览器进程并根据发送的指令生成页面然后显示到浏览器上


补充附录:
我们考虑上HTML / CSS / JS / DOM / IMG 的情况下页面加载顺序:

  • 1)解析HTML结构:html → head → title → #text(网页标题)
  • 2)加载外部脚本和样式表文件(CSS): → style → 加载样式 → 解析样式 → link → 加载外部样式表文件 → 解析外部样式表 (CSS)
  • 3)解析并执行脚本代码(JAVASCRIPT):→ script → 加载外部脚本文件 → 解析外部脚本文件 → 执行外部脚本 (Javascript)
  • 4)构造HTML DOM模型:→ body → div → script → 加载脚本 → 解析脚本 → 执行脚本
  • 5)加载图片等外部文件:→ img → script → 加载脚本 → 解析脚本 → 执行脚本
  • 6)页面加载完毕:→ 加载外部图像文件 → 页面初始化完毕 → JS 的初始化装载。

浏览器加载显示html的顺序是按下面的顺序进行的:

  • 1、IE下载的顺序是从上到下,渲染的顺序也是从上到下并且下载和渲染是同时进行的。
  • 2、在渲染到页面的某一部分时,其上面的所有部分都已经下载完成(并不是说所有相关联的元素都已经下载完)。
  • 3、如果遇到语义解释性的标签嵌入文件(JS脚本,CSS样式),那么此时IE的下载过程会启用单独连接进行下载。
  • 4、并且在下载后进行解析,解析过程中,停止页面所有往下元素的下载。
  • 5、样式表在下载完成后,将和以前下载的所有样式表一起进行解析,解析完成后,将对此前所有元素(含以前已经渲染的)重新进行渲染。
  • 6、JS、CSS中如有重定义,后定义函数将覆盖前定义函数。

简单地说,页面渲染就是浏览器将html代码根据CSS定义的规则显示在浏览器窗口中的这个过程。

1
2
3
4
5
6
7
8
9
10
11
1. 用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件;
2. 浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件;
3. 浏览器又发出CSS文件的请求,服务器返回这个CSS文件;
4. 浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了;
5. 浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;
6. 服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;
7. 浏览器发现了一个包含一行Javascript代码的<script>标签,赶快运行它;
8. Javascript脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”),突然就少了这么一个元素,浏览器不得不重新渲染这部分代码;
9. 终于等到了</html>的到来,浏览器泪流满面:
10. 这时用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径;
11. 浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过”,浏览器向服务器请求了新的CSS文件,重新渲染页面。


面试常问:

1.浏览器渲染过程是怎样的?

WeiyiGeek.

WeiyiGeek.

2.如何理解回流和重绘?

  • 重绘:对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)该过程叫做重绘 (常常是JS操作DOM)
  • 回流:对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来该过程就是回流(也叫重排)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #常见的会导致回流的元素
    1) 常见的几何属性有 width、height、padding、margin、left、top、border 等等。

    2) 最容易被忽略的操作:获取一些需要通过即时计算得到的属性,当你要用到像这样的属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 时,浏览器为了获取这些值,也会进行回流。

    3) 当我们调用了 getComputedStyle 方法,或者 IE 里的 currentStyle 时,也会触发回流。原理是一样的,都为求一个“即时性”和“准确性”。

    #避免方式:
    1) 避免逐条改变样式,使用类名去合并样式
    2) 将 DOM “离线”,使用DocumentFragment
    3) 提升为合成层,如使用will-change
    \#divId {
    will-change: transform;
    }

这样做的优点:

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快
  • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层
  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint

总结:

  • 重绘不一定导致回流,回流一定会导致重绘。
  • 部分浏览器缓存了一个 flush 队列,把我们触发的回流与重绘任务都塞进去,待到队列里的任务多起来、或者达到了一定的时间间隔,或者“不得已”的时候,再将这些任务一口气完成;但是当我们访问一些即使属性时,浏览器会为了获得此时此刻的、最准确的属性值,而提前将 flush 队列的任务出队。

3.渲染引擎什么情况下才会为特定的节点创建新的图层?
描述:层叠上下文是HTML元素的三维概念,这些HTML元素在一条假想的相对于面向(电脑屏幕的)视窗或者网页的用户的z轴上延伸,HTML元素依据其自身属性按照优先级顺序占用层叠上下文的空间。

1
2
3
1.拥有层叠上下文属性的元素会被提升为单独的一层,拥有层叠上下文属性有`根元素 (HTML),z-index 值不为 "auto"的 绝对/相对定位元素,position,transform 属性值不为 "none"的元素等`

2.需要剪裁(clip)的地方也会被创建为图层。当我们设置一个DIV元素的长宽时候如果文字超出我们设定的长宽便会产生剪裁,并且渲染引擎会把裁剪文字内容的一部分用于显示在 div 区域;出现这种裁剪情况的时候,渲染引擎会为文字部分单独创建一个层,如果出现滚动条也会被提升为单独的层。

WeiyiGeek.

WeiyiGeek.

4.JavaScript 是如何支持块级作用域的?
描述:块级作用域就是通过词法环境的栈结构来实现的,而变量提升是通过变量环境来实现,通过这两者的结合JavaScript 引擎也就同时支持了变量提升和块级作用域了,词法环境跟函数上下文,都是通过栈结构实现的;

  • 函数内部通过 var 声明的变量,在编译阶段全都被存放到变量环境(函数上下文)中,而通过let和const申明的变量会被追加到词法环境中,当这个块执行结束之后,追加到词法作用域的内容又会销毁掉。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //示例1.
    function foo() {
    var test = 1
    let myname= 'LuckyWinty'
    {
    console.log(myname)
    let myname= 'winty'
    }
    console.log(test,'---',myname)
    }
    foo()

    //实例2.
    function foo() {
    var test = 1
    let myname= 'LuckyWinty'
    try{
    {
    console.log(myname)
    let myname= 'winty'
    }
    }catch(ex){
    console.error(ex)
    }
    console.log(test,'---',myname)
    }
    foo()

    //分别思考一下会输出什么?

示例1.第一个console.log理论上应该输出 undefined。但是语法规定了一个”暂时性死区(TDZ,当进入它的作用域,它不能被访问(获取或设置)直到执行到达声明)”,也就是说虽然通过let声明的变量已经在词法环境中了,但是在没有赋值之前,访问该变量JavaScript引擎就会抛出一个错误。因此第一个console.log会抛错 [Uncaught ReferenceError: Cannot access 'myname' before initialization]

示例2.此,{}块作用域中的内容已执行完毕,被销毁掉了,第二个console.log会输出1 “—“ “LuckyWinty”。

WeiyiGeek.

WeiyiGeek.

JavaScript 中的数据是如何存储在内存中的?

  • 在 JavaScript 中,原始类型的赋值会完整复制变量值,而引用类型的赋值是复制引用地址。
  • 在 JavaScript 的执行过程中, 主要有三种类型内存空间,分别是代码空间、栈空间、堆空间。其中的代码空间主要是存储可执行代码的,原始类型(Number、String、Null、Undefined、Boolean、Symbol、BigInt)的数据值都是直接保存在“栈”中的,引用类型(Object)的值是存放在“堆”中的。因此在栈空间中(执行上下文),原始类型存储的是变量的值,而引用类型存储的是其在”堆空间”中的地址,当 JavaScript 需要访问该数据的时候,是通过栈中的引用地址来访问的,相当于多了一道转手流程。
  • 在编译过程中,如果 JavaScript 引擎判断到一个闭包,也会在堆空间创建换一个“closure(fn)”的对象(这是一个内部对象,JavaScript 是无法访问的),用来保存闭包中的变量。所以闭包中的变量是存储在“堆空间”中的。

JavaScript 引擎需要用栈来维护程序执行期间上下文的状态,如果栈空间大了话,所有的数据都存放在栈空间里面,那么会影响到上下文切换的效率,进而又影响到整个程序的执行效率。通常情况下,栈空间都不会设置太大,主要用来存放一些原始类型的小数据。而引用类型的数据占用的空间都比较大,所以这一类数据会被存放到堆中,堆空间很大,能存放很多大的数据,不过缺点是分配内存和回收内存都会占用一定的时间。因此需要“栈”和“堆”两种空间。


0x03 Web名词解释

QPS

  • Queries Per Second每秒查询数,即每秒能够响应的查询次数也是是最大吞吐能力。
  • QPS是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准,作为域名系统服务器的机器的性能经常用每秒查询率来衡量。


RPS

  • Requests Per Second 的缩写,代表吞吐率
  • 吞吐率是服务器并发处理能力的量化描述,单位是 reqs/s,指的是某个并发用户数下单位时间内处理的请求数。某个并发用户数下单位时间内能处理的最大的请求数,称之为最大吞吐率。
  • 提示:RPS/QPS,可以使用apche ab工具进行测量


TPS

  • Transactions Per Second 每秒处理的事务数目。
  • 一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程; 客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数,最终利用这些信息作出的评估分。
  • TPS 过程包括:客户端请求服务端、服务端内部处理、服务端返回客户端。
    例如,访问一个 Index 页面会请求服务器 3 次,包括一次 html,一次 css,一次 js,那么访问这一个页面就会产生一个“T”,产生三个“Q”。


PV

  • page view 即页面浏览量,通常是衡量一个网络新闻频道或网站甚至一条网络新闻的主要指标。
  • 用户每一次对网站中的每个页面访问均被记录 1 次。用户对同一页面的多次刷新,访问量累计。


RV

  • repeat visitors 即重复访问者数量


UV

  • Unique Visitor 访问数指独立访客访问数,统计1天内访问某站点的用户数(以 cookie 为依据),一台电脑终端为一个访客。


IP

  • Internet Protocol独立 IP 数,是指 1 天内多少个独立的 IP 浏览了页面,即统计不同的 IP 浏览用户数量。
  • 同一 IP 不管访问了几个页面独立 IP 数均为 1;不同的 IP 浏览页面计数会加 1


GMV

  • Gross Merchandise Volume 的简称
  • 只要是订单,不管消费者是否付款、卖家是否发货、是否退货,都可放进 GMV 。

参考链接:

浏览器相关原理(面试题)详细总结二