2

前提知识

1.TCP报文段首部格式

TCP.png
几个常见控制位:
确认位ACK: ACK=1时确认号有效,在连接建立后所有传送的报文段都必须把ACK置为1。
同步位SYN: SYN=1时, 表明是一个连接请求/连接接受报文。
终止位FIN: FIN=1时, 表明此报文段发送方数据已发完,要求释放连接。

2.TCP建立连接的过程

连接传输.png
TCP 连接传输的三个阶段:连接建立——>数据传送——>连接释放

3.浏览器内核

分为两个部:一是渲染引擎,另一个是JS引擎。
常见的浏览器内核:Trident(IE)、Gecko(火狐)、Blink(Opera)、Webkit(Safari、Chrome)。

从输入URL到页面加载全过程

简单来说就是1.浏览器下载资源;2.浏览器渲染页面。

1.浏览器分析URL

URL(统一资源定位符)一般形式:URL不区分大小写
<协议>://<主机>:<端口>/<路径>
http 域名
https IP地址
ftp
file

URLURI 的区别:
URL:统一资源定位符。通过路径确定资源。
URI:统一资源标识符。唯一标识一个资源。
URI = URL + URK

2.浏览器向DNS请求解析IP地址

DNS 域名解析系统。

域名解析过程有两种:1. 递归查询。2. 迭代查询
域名解析.png
高速缓存:定期更新。当浏览器访问过一域名后会在本地域名服务器中缓存这个地址,下次直接访问即可。
比如:要查找efg.com 域名时直接到dns.com顶级域名服务器查找,不必在先去根域名服务器查找。

3.浏览器与服务器建立TCP连接

连接建立:采用客户服务器方式(三次握手)

建立TCP.png
ROUND 1:
客户端发送连接请求报文段,无应用层数据。SYN = 1,seq =x(随机)
同步位SYN:SYN = 1时,表明是一个连接请求/连接接受报文。
ROUND 2:
服务器端该TCP连接分配缓存和变量,并向客户端返回确认报文段,允许连接,无应用层数据。
SYN=1,ACK=1,seq=y(随机),ack=x+1
ACK=1时确认号有效,在连接建立后所有传送的报文段都必须把ACK置为1。
ROUND 3:
客户端为该TCP连接分配缓存和变t,并向服务器端返回确认的确认,可以携带数据。
SYN=0,ACK-1,seq=x+1, ack=y+1

4.浏览器发出取文件命令

发起HTTP请求,发送请求报文:
请求报文.png
//某浏览器发出的请求报文
GET /index .html HTTP/1.1
Host: www.test. edu.cn
Connection: Close
Cookie: 123456

5.服务器响应

状态码

6.释放TCP连接(4次挥手)

释放TCP.png

ROUND 1:
客户端发送连接释放报文段,停止发送数据,主动关闭TCP连接。FIN=1,seq=u
ROUND 2:
服务器端回送一个确认报文段,客户到服务器这个方向的连接就释放了一一半关闭状态。ACK=1,seq=v, ack=u+1
ROUND 3:
服务器端发完数据,就发出连接释放报文段,主动关闭TCP连接。FIN=1,ACK=1, seq=w, ack=u+1
ROUND 4:
客户端回送一个确认报文段,再等到时间等待计时器设置的2MSL (最长报文段寿命)后,连接彻底关闭。ACK=1,seq=u+1,ack=w+1

7.浏览器渲染

关键渲染路径是指浏览器请求下载资源,然后解析、构建树、渲染布局、绘制,最后呈现给客户界面的整个过程。
用户看到页面实际上可以分为两个阶段:页面内容加载完成和页面资源加载完成,分别对应于 DOMContentLoadedLoad
window.onload 和DOMContentLoaded的区别。

window.addEventListener("load",function(){
// 页面的全部资源加载完才会执行,包括图片、视频等。
})
document.addEventListener("DOMContentLoaded",function(){
//DOM 渲染完即可执行,此时样式表、图片、视频还可能没有加载完(比较好)
})

浏览器渲染的过程主要包括以下五步:

渲染过程.png

  1. 浏览器将获取的HTML文档解析成DOM树。
  2. 处理CSS标记,构成层叠样式表模型CSSOM。
  3. 将DOM和CSSOM合并为渲染树(Render tree)。
  4. 计算渲染树中每个DOM元素的坐标和大小,它被称之为布局layout。浏览器使用一种流式处理的方法,只需要一次绘制操作就可以布局所有的元素。
  5. 将渲染树的各个节点绘制到屏幕上,这一步被称为绘制painting。

浏览器渲染网页的具体流程

构建DOM树

当浏览器接收到服务器响应来的HTML文档后,会遍历文档节点,生成DOM树。
需要注意以下几点:

  • DOM树在构建的过程中可能会被CSS和JS的加载而执行阻塞
  • display:none的元素也会在DOM树中
  • 注释也会在DOM树中
  • script标签会在DOM树中

无论是DOM还是CSSOM,都是要经过Bytescharacterstokensnodesobjectmodel这个过程。
构建DOM树.png
当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。

构建CSSOM规则树

浏览器解析CSS文件并生成CSSOM,每个CSS文件都被分析成一个StyleSheet对象,每个对象都包含CSS规则。CSS规则对象包含对应于CSS语法的选择器和声明对象以及其他对象。
在这个过程需要注意的是:

  • CSS解析可以与DOM解析同时进行。
  • CSS解析与script的执行互斥 。
  • 在Webkit内核中进行了script执行优化,只有在JS访问CSS时才会发生互斥。

构建渲染树(Render Tree)

通过DOM树和CSS规则树,浏览器就可以通过它两构建渲染树了。浏览器会先从DOM树的根节点开始遍历每个可见节点,然后对每个可见节点找到适配的CSS样式规则并应用。
有以下几点需要注意:

  • Render Tree和DOM Tree不完全对应
  • display: none的元素不在Render Tree中
  • visibility: hidden的元素在Render Tree中

构建渲染树.png
渲染树生成后,还是没有办法渲染到屏幕上,渲染到屏幕需要得到各个节点的位置信息,这就需要布局(Layout)的处理了。

渲染树布局(layout of the render tree)

定位DOM元素坐标和大小

布局阶段会从渲染树的根节点开始遍历,由于渲染树的每个节点都是一个Render Object对象,包含宽高,位置,背景色等样式信息。所以浏览器就可以通过这些样式信息来确定每个节点对象在页面上的确切大小和位置,布局阶段的输出就是我们常说的盒子模型,它会精确地捕获每个元素在屏幕内的确切位置与大小。需要注意的是:

  • float元素,absoulte元素,fixed元素会发生位置偏移。
  • 我们常说的脱离文档流,其实就是脱离Render Tree。

渲染树绘制(Painting the render tree)

在绘制阶段,浏览器会遍历渲染树,调用渲染器的paint()方法在屏幕上显示其内容。渲染树的绘制工作是由浏览器的UI后端组件完成的。

常见知识点

浏览器如果渲染过程中遇到JS文件怎么处理?

  • 渲染过程中,如果遇到<script>就停止渲染,执行 JS 代码。JavaScript的加载、解析与执行会阻塞DOM的构建。(将 script 标签放在 body 标签底部的原因)
  • JS文件不只是阻塞DOM的构建,它会导致CSSOM也阻塞DOM的构建。CSS阻塞渲染意味着,在CSSOM完备前,页面将一直处理白屏状态,这就是为什么样式放在head中,仅仅是为了更快的解析CSS,保证更快的首次渲染。需要注意的是,即便你没有给页面任何的样式声明,CSSOM依然会生成,默认生成的CSSOM自带浏览器默认样式。

原因:
如果JavaScript想访问CSSOM并更改它,那么在执行JavaScript时,必须要能拿到完整的CSSOM。所以此时浏览器会先下载和构建CSSOM,然后再执行JavaScript,最后在继续构建DOM。

回流(重排)和重绘(reflow和repaint)

HTML默认是流式布局的,但CSS和JS会打破这种布局,改变DOM的外观样式以及大小和位置。因此我们就需要知道两个概念:reflowreplaint

  • 回流(重排):对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(重排)。
  • 重绘:当我们对 DOM 的修改导致了样式的变化(比如修改了颜色或背景色)却并未影响其几何属性时,浏览器不需重新计算元素的几何属性,直接为该元素绘制新的样式(跳过了重排环节)。

当网页生成的时候,至少会渲染一次。在用户访问的过程中,还会不断重新渲染。重新渲染会重复回流+重绘或者只有重绘
回流(重排)必定会发生重绘,重绘不一定会引发回流。重绘和回流会在我们设置节点样式时频繁出现,同时也会很大程度上影响性能。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。

常见引起回流(重排)属性和方法

任何会改变元素几何信息(元素的位置和尺寸大小)的操作,都会触发回流。
  • 添加或者删除可见的DOM元素;
  • 元素尺寸改变——边距、填充、边框、宽度和高度
  • 内容变化,比如用户在input框中输入文字
  • 浏览器窗口尺寸改变——resize事件发生时
  • 计算 offsetWidth 和 offsetHeight 属性
  • 设置 style 属性的值

常见引起重绘属性和方法

color、border-style、visibility、background、text-decoration等

减少reflow、repaint触发次数

  • transform做形变和位移可以减少reflow
  • 使用 visibility 替换 display: none
  • 避免逐个修改节点样式,尽量一次性修改。
使用DocumentFragment将需要多次修改的DOM元素缓存,最后一次性append到真实DOM中渲染
  • CSS 选择符避免节点层级过多
  • 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局。
  • 避免多次读取某些属性。

async和defer

1)情况1

<script src="script.js"></script>

没有 defer 或 async,浏览器会立即加载并执行指定的脚本,

2)情况2

<script async src="script.js"></script> (异步下载)

async是异步下载脚本文件,下载完毕立即解释执行代码。如果是多个,执行顺序和加载顺序无关

3)情况3

<script defer src="script.js"></script>(延迟执行)

defer是在HTML解析完之后才会执行,如果是多个,按照加载的顺序依次执行

为什么操作 DOM 慢?

用 JS 去操作 DOM 时,本质上是 JS 引擎和渲染引擎之间进行了“跨界交流”。它依赖了桥接接口作为“桥梁”,我们每操作一次 DOM,都要过一次“桥”。过“桥”的次数一多,就会产生比较明显的性能问题。因此“减少 DOM 操作”。
js-xuanr.png

基于浏览器渲染原理的性能优化策略?

  • CSS放在head中,JS放在body最下面
  • 懒加载(图片懒加载,上滑加载更多)
  • 用DOMContentLoaded触发执行JS
  • 减少reflow、repaint触发次数
  • 非核心代码异步加载

参考文章:https://github.com/ljianshu/B...
参考文章:https://www.jianshu.com/p/e62...


梁柱
135 声望12 粉丝