Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

性能优化 #167

Open
coconilu opened this issue Jun 23, 2020 · 0 comments
Open

性能优化 #167

coconilu opened this issue Jun 23, 2020 · 0 comments

Comments

@coconilu
Copy link
Owner

coconilu commented Jun 23, 2020

性能优化

在互联网,速度是关键。很多案例已经证明:

  • 网站越快,用户的黏性越高
  • 网站越快,用户忠诚度越高
  • 网站越快,用户转化率越高

在前端领域,性能优化有两个主要目的:

  1. 加载要快,包括静态资源下载速度、服务器响应动态资源的速度
  2. 响应要顺畅,用户的操作要即时响应,并且给予提示

优化网络请求

目前,一个普通的Web应用大约就有1MB,有100个左右的资源分散在15台不同的主机上。优化网络请求势在必行。

延迟和带宽

对于网络请求,有两个指标指示请求的质量:延迟和带宽。

延迟,分组从信息源发送到目的地所需的时间
带宽,逻辑或物理通信路径最大的吞吐量

客户端到服务器的总延迟时间包括:

  1. 传播延迟,消息从发送端到接收端需要的时间,是信号传播距离和速度的函数,影响延迟的主要元素
  2. 传输延迟,把消息中的所有比特转移到链路中所需的时间,是消息长度和链路速度的函数
  3. 处理延迟,处理分组首部、检查位错误及确定分组目标所需的时间,这部分时间主要是花费在路由器里
  4. 排队延迟,到来的分组排队等待处理的时间

对于带宽,目前小区带宽普遍百兆光纤,4G移动网络,就上下行速率来说,可以看成是一个百兆级的宽带,现在比较普遍的100M宽带的下行是100Mbps,上行是20Mbps。

那么延迟和带宽,哪个才是影响网络体验的关键呢?

可以看看下图,某网址首页加载html静态资源的Timing图:

Image

蓝色部分是下载数据需要的时间,它是受带宽影响的;其它部分都是延迟影响的,特别是绿色部分(Waiting),它是受传播延迟影响的,橙色部分是建立TCP连接花费的时间,紫色部分是其中建立SSL花费的世界。很显然,在现在互联网时代,带宽不再是网络体验的瓶颈,延迟才是。

TTFT,Time To First Byte,接收到第一个字节所需要的时间

如何降低延迟

让我们理清资源交互过程。一次请求过程,包括建立TCP、TLS,然后是发送HTTP报文,等待HTTP响应。

对于TCP

建立TCP连接需要三次握手,那么就相当于放大三倍延迟,所以提高TCP应用性能的关键,在于想办法重用连接,当然,如果能从逻辑上减少握手的步骤也是一种策略,TFO就是其中一种策略。

TFO(TCP Fast Open,TCP快速打开)通过握手开始时的SYN包中的TFO cookie(一个TCP选项)来验证一个之前连接过的客户端。如果验证成功,它可以在三次握手最终的ACK包收到之前就开始发送数据,这样便跳过了一个绕路的行为,更在传输开始时就降低了延迟。

重用TCP连接,目前已经广泛使用在HTTP1.1协议,保持长连接(Connection: keep-alive)便可以重用TCP连接,减少建立TCP花费的世界。

当然,如果使用UDP替代TCP将会彻底减少建立连接需要花费的世界,QUIC(快速UDP网络连接)就是基于这个原理。

在QUIC广泛应用之前,我们可以使用HTTP2达到最大程度重用TCP连接的目的。HTTP1.1时代为了加速网络访问,浏览器会为每个域名同时建立最多6个TCP连接用以摆脱HTTP队首阻塞。HTTP2是基于帧的,可以多路复用TCP连接,同域名的多个请求可以都重用TCP连接,减少建立TCP连接的耗时。

对于SSL/TLS

SSL/TLS的握手过程也是比较复杂的。大致过程就是,确定加密套件,校验服务器公钥,生成3个随机数,通过三个随机数生成对称加密密钥,校验密钥,传输加密信息。

为了减少握手的耗时。如果某次握手过程的 Client Hello 消息里还附带了上一次的 Session ID,服务端接收到这个 Session ID 并校验后如果能复用密钥就不再进行后续的握手过程。

ALPN(应用层协议协商)作为TLS扩展,能过在TLS握手的同事协商应用协议,从而可以省掉HTTP的Upgrade机制所需的额外往返时间。

静态资源

延迟是跟距离相关的,原则上说,距离越短延迟越低,CDN服务就是基于这个原理。

把静态资源放在CDN服务上,可以有效降低静态资源请求的延迟。

CDN的核心是智能调度,它的底层实现原理依赖运营商,因为运营商有用户IP地址的元信息,还知道哪些地区有哪些IP可以提供服务。所以,在域名解析阶段去分析用户IP,返回最近最佳的服务器IP。

除了CDN,使用缓存可以通过减少请求次数达到减少延迟的效果。

HTTP协议

对于HTTP1.X而已,优化是基于TCP协议特性的,具体策略是减少建立连接次数、减少请求数量、提升并发下载能力:

  1. 建立持久连接,复用TCP连接,HTTP1.1默认保持长连接
  2. 使用HTTP管道,把请求提前传到服务器,但还是会有队首阻塞
  3. 多个TCP连接并行加载资源,浏览器会为一个域名最多建立6个TCP连接
  4. 域名分区,为了突破一个域名最多建立6个TCP连接而提升并行下载资源的能力
  5. 连接与拼合,把多个JS或CSS文件组合成一个文件,把多张图片组合为一张更大的复合的图片,有一个缺点就是合并的资源里有一个失效了,会导致其它资源缓存失效
  6. 小段JS和CSS代码可以直接嵌入页面,小图片也可以使用base64嵌入网页,用于减少请求次数

HTTP管道,原理是把FIFO队列从客户端(请求队列)迁移到服务器(响应队列)

对于HTTP2可以有效解决HTTP1.X遇到的问题,HTTP管道不会再发生队首阻塞,多路复用不需要建立多个TCP连接和域名分区,可以有效缓存小块资源不需要连接与拼合。

优化用户体验

良好的用户体验可以降低用户对延迟的敏感度。

浏览器优化

现代浏览器可以为我们做很多的优化:

  1. 基于文档的优化
    1. 资源预取和排定优先次序,发现和优先安排关键网络资源,今早分派请求并取得页面,使其尽快达到可交互的状态
  2. 推测性优化
    1. DNS预解析,可以通过学习导航历史、用户的鼠标悬停,或其它页面信号来触发
    2. TCP预连接,DNS解析之后,浏览器可以根据预测的HTTP请求,推测性地打开TCP连接
    3. 页面预渲染,在隐藏的标签页中预先渲染整个页面

现代浏览器的标签可以指示浏览器提前记载资源,它的rel属性有如下值:dns-prefetch、prefetch、prerender,分别指示浏览器预解析特定的域名、预取得将来导航要用的资源、根据对用户下一个目标的预测,预渲染特定页面

图片优化

前端性能优化最重要的部分是图片优化。很多网站,图片资源的大小比JS+CSS还大。

图片优化主要有以下手段:

  1. 懒加载图片,懒加载作为图片优化的主力军广泛适用各种场景,对于比较长的页面,用户不一定每次都会下滑到底部,那么懒加载便能节省下不需要展示的图片的带宽
  2. 预加载图片,为了让图片今早显示在用户面前,预加载可以通过提前加载图片资源,当真正要去拉取图片资源的时候便可以在缓存中找到
  3. 响应式图片,把图片裁剪出不同尺寸的图片,供不同的场景使用

动画优化

尽量使用硬件加速,下面三个style属性会使用GPU处理:

  1. transform
  2. opacity
  3. filter

现代浏览器还支持使用 will-change 或 translateZ 把元素提升到一个独立的层。

如果一定要使用JS接口,请使用requestAnimationFrame(callback)接口,callback的回调带有一个参数——时间戳,值跟performance.now()的返回值相同,可以借助这个时间戳计算动画量。

交互优化

浏览器上有一些事件是会持续发生的,比如滚动事件、拖拽事件、resize事件等等,这些事件会在每一帧开始前执行事件回调。

如果不是在这些事件上驱动动画的话,建议使用防抖节流的策略去处理事件。

防抖是将多次执行变为最后一次执行。

节流是将多次执行变为在规定时间内只执行一次。

可以通过下图看函数正常执行、防抖、节流的区别:

2

防抖适合的业务:

非立即执行版:input搜索框,客户输完过一会就会自动搜索;window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次;立即执行版:就是对于按钮防点击,例如点赞、心标、收藏等有立即反馈的按钮。

节流适合的业务:

鼠标不断点击触发,点击事件在规定时间内只触发一次(单位时间内只触发一次);监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断。

DOM接口

DOM是浏览器提供的描述文档元素的对象。

通过选择器去查询dom元素是比较耗时的,最好的做法就是用变量保存它。

关于回流和重绘,每当我们去修改一个dom元素的样式(包括几何尺寸和颜色),这个元素会被标记为dirty,并把更新操作(Recalc Styles、Layout【如果是重绘则跳过】、Update Layer Tree、Paint、Composite)放入下一帧更新队列里,浏览器会在合适的时机去更新dom元素。

几何尺寸包括:width、height、padding、margin、left、top、border等
颜色指背景色、字体色等等

如果此时访问元素的几何、定位属性,如offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight,就会触发强制回流、重绘,元素的dom.getBoundingClientRect();方法也会触发。

观察如下的代码,和它们的执行过程火焰图:

function test1() {
    var $0 = document.querySelector("#sidebar")
    $0.style.width = "800px";
}

setTimeout(test1);

观察函数执行过程:

3

function test1() {
    var $0 = document.querySelector("#sidebar")
    $0.style.width = "800px";
    $0.getBoundingClientRect();
}

setTimeout(test1);

4e

对比可以看出来,第二张图在函数test1里执行强制回流重绘,导致test1执行时间变长,这是兵家大忌。

使用3D加速的CSS属性之所以很快且流畅,是因为动画属性一经提交到GPU就不会再占用主线程了,也就是说该动画的整个过程不会触发Recalc Styles、Layout、Update Layer Tree、Paint、Composite。

参考

《Web性能权威指南》
《前端性能优化原理与实践》,掘金

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant