前端性能优化知识梳理

49,198 阅读32分钟

1.重要性

当我们面试的时候,前端性能优化方面算是必考的知识点,但是工作中我们又很少会重点的对项目进行前端优化,它真的不重要吗?

如果我们可以将后端响应时间缩短一半,整体响应时间只能减少5%~10%。而如果关注前端性能,同样是将其响应时间减少一半,则整体响应时间可以减少40%~45%。

改进前端通常只需要较少的时间和资源,减少后端延迟会带来很大的改动。

只有10%~20%的最终用户响应时间花在了下载HTML文档上,其余的80%~90%时间花在了下载页面中的所有组件上。

所以前端开发中的性能优化对我们有哪些提升呢?

  • 用户体验:用户希望网站能够快速加载和相应,如果网站加载速度慢,用户可能会感到不耐烦而离开网站。
  • 减少服务器负载:优化前端性能可以减少服务器的负载,从而减少服务器的成本和维护成本。
  • 节省带宽:优化前端性能可以减少网站带宽的使用,从而减少带宽成本。
  • 提高转化率:快速加载的网站可以提高用户的满意度和转化率,从而增加收入。

当然,多我们前端开发者来说,这些点中用户体验对我们来说是极为重要的。

2.定位

2.1 技术上的选择

在前端日常开发中,技术上的选择是非常重要的。为什么要讲这个呢?因为现象频发。

前端工程化严重的当下,轻量化的框架慢慢被遗忘掉了。并不是所有的业务场景都适合使用工程化框架,react/vue 并不轻量。

复杂的框架是为了解决复杂的业务

如果研发h5、PC展示等场景简单的业务时候,javascript原生 配合一些轻量化插件更适合。

多页面应用也并不都是缺点。根据业务不同而选择不一样的技术是非常重要的,是每个前端都应该反思的事情。

这方面是导致卡顿的关键问题。

2.2 NetWork

我们的老朋友NetWork想必前端同学都很熟悉。我们先来看一下network面板

从面板上我们可以看出一些信息:

  • 请求资源size
  • 请求资源时长
  • 请求资源数量
  • 接口响应时长
  • 接口发起数量
  • 接口报文size
  • 接口响应状态
  • 瀑布图

瀑布图是什么呢?

瀑布图就是上方图片后面的waterfall纵列

瀑布图是一个级联图, 展示了浏览器如何加载资源并渲染成网页. 图中的每一行都是一次单独的浏览器请求. 这个图越长, 说明加载网页过程中所发的请求越多. 每一行的宽度, 代表浏览器发出请求并下载该资源的过程中所耗费的时间。它的侧重点在于分析网路链路

瀑布图颜色说明:

  • DNS Lookup [深绿色] - 在浏览器和服务器进行通信之前, 必须经过DNS查询, 将域名转换成IP地址. 在这个阶段, 你可以处理的东西很少. 但幸运的是, 并非所有的请求都需要经过这一阶段.

  • Initial Connection [橙色] - 在浏览器发送请求之前, 必须建立TCP连接. 这个过程仅仅发生在瀑布图中的开头几行, 否则这就是个性能问题(后边细说).

  • SSL/TLS Negotiation [紫色] - 如果你的页面是通过SSL/TLS这类安全协议加载资源, 这段时间就是浏览器建立安全连接的过程. 目前Google将HTTPS作为其 搜索排名因素 之一, SSL/TLS 协商的使用变得越来越普遍了.

  • Time To First Byte (TTFB) [绿色] - TTFB 是浏览器请求发送到服务器的时间+服务器处理请求时间+响应报文的第一字节到达浏览器的时间. 我们用这个指标来判断你的web服务器是否性能不够, 或者说你是否需要使用CDN.

  • Downloading (蓝色) - 这是浏览器用来下载资源所用的时间. 这段时间越长, 说明资源越大. 理想情况下, 你可以通过控制资源的大小来控制这段时间的长度.

那么除了瀑布图的长度外,我们如何才能判断一个瀑布图的状态是健康的呢?

  • 首先, 减少所有资源的加载时间. 亦即减小瀑布图的宽度. 瀑布图越窄, 网站的访问速度越快.

  • 其次, 减少请求数量 也就是降低瀑布图的高度. 瀑布图越矮越好.

  • 最后, 通过优化资源请求顺序来加快渲染时间. 从图上看, 就是将绿色的"开始渲染"线向左移. 这条线向左移动的越远越好.

这样,我们就可以从network的角度去排查“慢”的问题。

2.3 webpack-bundle-analyzer

项目构建后生成的bundle包是压缩后的。webpack-bundle-analyzer是一款包分析工具。

我们先来看一下它能带来的效果。如下图:

从上图来看,我们的bundle包被解析的一览无余。其中模块面积占的越大说明在bundle包中size越大。就值得注意了,重点优化一下。

它能够排查出来的信息有

  • 显示包中所有打入的模块
  • 显示模块size 及 gzip后的size

排查包中的模块情形是非常有必要的,通过webpack-bundle-analyzer来排查出一些无用的模块,过大的模块。然后进行优化。以减少我们的bundle包size,减少加载时长。

安装

# NPM 
npm install --save-dev webpack-bundle-analyzer
# Yarn 
yarn add -D webpack-bundle-analyzer

使用(as a Webpack-Plugin)

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
 
module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

然后构建包完毕后会自动弹出一个窗口展示上图信息。

2.4 Performance

chrome自带的performance模块。先附上一个官网文档传送门:Performance

可以检测很多方面的数据,多数情况的性能排查上用的比较多。如果想要深入了解的同学建议去看一下官方文档。

接下来我们来说一下在performance面板中如何排差“慢”的问题,它给我们提供了哪些信息呢。先附上一张performance的面板图片。

从上图中可以分析出一些指标

  • FCP/LCP 时间是否过长?
  • 请求并发情况 是否并发频繁?
  • 请求发起顺序 请求发起顺序是否不对?
  • javascript执行情况 javascript执行是否过慢?

这些指标就是我们需要重点关注的,当然performance的功能并不止于此。

先记住如何获取到这些指标,后面来一一进行解析优化。

2.5 性能监视器

首先打开F12,随后按快捷键 Ctrl + Shift + P,然后输入性能监视器/monitor。

数据非常直观,浏览网页的时候可以打开,以供参考。

2.6 JavaScript性能剖析器

首先打开F12,随后按快捷键 Ctrl + Shift + P,然后输入性能剖析器/profiler。

我们点击开始后浏览我们的网页,就会出现一份性能剖析报告。

2.7 Recorder

这是一个很棒的功能,Recorder顾名思义---记录员,他可以记录下用户和网页的交互(意念艾特了下公司的交互)。

首先打开F12,随后按快捷键 Ctrl + Shift + P,然后输入Recorder。

点击开始,然后输入本次操作名称: 掘金黑色的枫主页交互梳理。

然后我们就可以到处点点看,就会记录下我们的操作。

点击结束之后可以结束录制,我们可以回访我们的操作,还可以对我们的操作进行分析。

分析中包括了屏幕截图,内存,网页指标,妥妥的精工利器。

从这里我们会了解到很多,比如事件日志,我们可以知道我们触发操作后都发生了什么。

而调用树,我们可以知道异步任务,定时器,动画帧,垃圾回收等的执行。

Recorder确实对我们网页分析有很大的帮助。

2.8 PerformanceNavigationTiming

获取各个阶段的响应时间,我们所要用到的接口是PerformanceNavigationTiming接口。

PerformanceNavigationTiming 提供了用于存储和检索有关浏览器文档事件的指标的方法和属性。 例如,此接口可用于确定加载或卸载文档需要多少时间。

function showNavigationDetails() {
  const [entry] = performance.getEntriesByType("navigation");
  console.table(entry.toJSON());
}

使用这个函数,我们就可以获取各个阶段的响应时间,如图:

在这里插入图片描述

参数说明

navigationStart 加载起始时间
redirectStart 重定向开始时间(如果发生了HTTP重定向,每次重定向都和当前文档同域的话,就返回开始重定向的fetchStart的值。其他情况,则返回0)
redirectEnd 重定向结束时间(如果发生了HTTP重定向,每次重定向都和当前文档同域的话,就返回最后一次重定向接受完数据的时间。其他情况则返回0)
fetchStart 浏览器发起资源请求时,如果有缓存,则返回读取缓存的开始时间
domainLookupStart 查询DNS的开始时间。如果请求没有发起DNS请求,如keep-alive,缓存等,则返回fetchStart
domainLookupEnd 查询DNS的结束时间。如果没有发起DNS请求,同上
connectStart 开始建立TCP请求的时间。如果请求是keep-alive,缓存等,则返回domainLookupEnd
(secureConnectionStart) 如果在进行TLS或SSL,则返回握手时间
connectEnd 完成TCP链接的时间。如果是keep-alive,缓存等,同connectStart
requestStart 发起请求的时间
responseStart 服务器开始响应的时间
domLoading 从图中看是开始渲染dom的时间,具体未知
domInteractive 未知
domContentLoadedEventStart 开始触发DomContentLoadedEvent事件的时间
domContentLoadedEventEnd DomContentLoadedEvent事件结束的时间
domComplete 从图中看是dom渲染完成时间,具体未知
loadEventStart 触发load的时间,如没有则返回0
loadEventEnd load事件执行完的时间,如没有则返回0
unloadEventStart unload事件触发的时间
unloadEventEnd unload事件执行完的时间

关于我们的Web性能,我们会用到的时间参数:

DNS解析时间: domainLookupEnd - domainLookupStart
TCP建立连接时间: connectEnd - connectStart
白屏时间: responseStart - navigationStart
dom渲染完成时间: domContentLoadedEventEnd - navigationStart
页面onload时间: loadEventEnd - navigationStart

根据这些时间参数,我们就可以判断哪一阶段对性能有影响。

2.9 抓包

有一些业务状况是没有上述的一些调试工具该怎么办呢?我们可以利用抓包工具进行对页面信息对抓取,上述我们通过chrome工具排查出来的指标,也可以通过抓包工具进行抓取。

这里我推荐一款抓包工具charles

2.10 Elements

打开F12时,我们查看当前页面,右侧可以看到当前页面的css以及事件监听器,我们经常可以看到有很多重复的CSS和监听器,我优化过一个项目,一个页面有上百个重复的CSS,resize监听器上面更是绑定了50+的监听事件。

这也是一个很好的优化点。

2.11 JSCPD

JSCPD是一种跨语言的代码复制检测工具,可以在检测和报告重复代码。

我们可以使用该工具对当前项目进行检测,查看重复代码以及无用代码,进行项目瘦身。

我记得这个是可以在npm上安装,然后会输出对应文档。

2.12. 浏览器的任务管理器

可以查看当前Chrome浏览器中,所有进程关于GPU,网络和内存空间的使用情况,这些进程包括当前打开的各个标签页,安装的各种扩展插件,以及GPU,网络,渲染等浏览器的默认进程,通过监控这些数据,可以定位可能存在内存泄露或网络资源加载异常的问题进程。

点击浏览器的最顶栏 -> 更多工具 -> 任务管理器

屏幕快照 2021-06-16 15.02.04.png

我们可以看到所有进行的进程,可以看到内存占用网络消耗。

2.13 性能测试工具

2.13.1 Pingdom

2.13.2 Load Impact

2.13.3 WebPage Test

2.13.4 Octa Gate Site Timer

2.13.5 Free Speed Test

2.13.6 Gople PageSpeed Insights

2.13.7 GTmetrix

2.13.8 YSlow

3.优化

前端的优化种类繁多,主要包含三个方面的优化:网络优化(对加载时所消耗的网络资源优化),代码优化(资源加载完后,脚本解释执行的速度),框架优化(选择性能较好的框架,比如benchmark)。

3.1 tree shaking

中文(摇树),webpack构建优化中重要一环。摇树用于清除我们项目中的一些无用代码,它依赖于ES中的模块语法。

比如日常使用lodash的时候

import _ from 'lodash'

如果如上引用lodash库,在构建包的时候是会把整个lodash包打入到我们的bundle包中的。

import _isEmpty from 'lodash/isEmpty';

如果如上引用lodash库,在构建包的时候只会把isEmpty这个方法抽离出来再打入到我们的bundle包中。

这样的化就会大大减少我们包的size。所以在日常引用第三方库的时候,需要注意导入的方式。

如何开启摇树

在webpack4.x 中默认对tree-shaking进行了支持。 在webpack2.x 中使用tree-shaking:传送门

3.2 split chunks

中文(分包)

在没配置任何东西的情况下,webpack 4 就智能的帮你做了代码分包。入口文件依赖的文件都被打包进了main.js,那些大于 30kb 的第三方包,如:echarts、xlsx、dropzone等都被单独打包成了一个个独立 bundle。

其它被我们设置了异步加载的页面或者组件变成了一个个chunk,也就是被打包成独立的bundle。

它内置的代码分割策略是这样的:

  • 新的 chunk 是否被共享或者是来自 node_modules 的模块
  • 新的 chunk 体积在压缩之前是否大于 30kb
  • 按需加载 chunk 的并发请求数量小于等于 5 个
  • 页面初始加载时的并发请求数量小于等于 3 个

大家可以根据自己的项目环境来更改配置。配置代码如下:

splitChunks({
  cacheGroups: {
    vendors: {
      name: `chunk-vendors`,
      test: /[\\/]node_modules[\\/]/,
      priority: -10,
      chunks: 'initial',
    },
    dll: {
      name: `chunk-dll`,
      test: /[\\/]bizcharts|[\\/]\@antv[\\/]data-set/,
      priority: 15,
      chunks: 'all',
      reuseExistingChunk: true
    },
    common: {
      name: `chunk-common`,
      minChunks: 2,
      priority: -20,
      chunks: 'all',
      reuseExistingChunk: true
    },
  }
})

没有使用webpack4.x版本的项目,依然可以通过按需加载的形式进行分包,使得我们的包分散开,提升加载性能。

按需加载也是以前分包的重要手段之一

这里推荐一篇非常好的文章:webpack如何使用按需加载

3.3 拆包

与3.2的分包不同。大家可能没发现,上面2.3的bundle包解析中有个有趣的现象,上面项目的技术栈是react,但是bundle包中并没有react、react-dom、react-router等。

因为把这些插件“拆”开了。并没有一起打在bundle中。而是放在了CDN上。下面我举一个例子来解释一下。

假设:原本bundle包为2M,一次请求拉取。拆分为 bundle(1M) + react桶(CDN)(1M) 两次请求并发拉取。

从这个角度来看,1+1的模式拉取资源更快。

换一个角度来说,全量部署项目的情况,每次部署bundle包都将重新拉取。比较浪费资源。react桶的方式可以命中强缓存,这样的化,就算全量部署也只需要重新拉取左侧1M的bundle包即可,节省了服务器资源。优化了加载速度。

注意:在本地开发过程中,react等资源建议不要引入CDN,开发过程中刷新频繁,会增加CDN服务其压力,走本地就好。

3.4 gzip

服务端配置gzip压缩后可大大缩减资源大小。

Nginx配置方式

http {
  gzip on;
  gzip_buffers 32 4K;
  gzip_comp_level 6;
  gzip_min_length 100;
  gzip_types application/javascript text/css text/xml;
  gzip_disable "MSIE [1-6]\.";
  gzip_vary on;
}

配置完成后在response header中可以查看。

3.5 图片压缩

开发中比较重要的一个环节,我司自己的图床工具是自带压缩功能的,压缩后直接上传到CDN上。

如果公司没有图床工具,我们该如何压缩图片呢?我推荐几种我常用的方式

  • 智图压缩 (百度很难搜到官网了,免费、批量、好用)
  • tinypng(免费、批量、速度块)
  • fireworks工具压缩像素点和尺寸 (自己动手,掌握尺度)
  • 找UI压缩后发给你

图片压缩是常用的手法,因为设备像素点的关系,UI给予的图片一般都是 x2,x4的,所以压缩就非常有必要。

3.6 图片分割

如果页面中有一张效果图,比如真机渲染图,UI手拿着刀不让你压缩。这时候不妨考虑一下分割图片。

建议单张土图片的大小不要超过100k,我们在分割完图片后,通过布局再拼接在一起。可以图片加载效率。

这里注意一点,分割后的每张图片一定要给height,否则网速慢的情况下样式会塌陷。

3.7 sprite

南方叫精灵图,北方叫雪碧图。这个现象就很有趣。

在网站中有很多小图片的时候,一定要把这些小图片合并为一张大的图片,然后通过background分割到需要展示的图片。

这样的好处是什么呢?先来普及一个规则

浏览器请求资源的时候,同源域名请求资源的时候有最大并发限制,chrome为6个,就比如你的页面上有10个相同CDN域名小图片,那么需要发起10次请求去拉取,分两次并发。第一次并发请求回来后,发起第二次并发。

如果你把10个小图片合并为一张大图片的画,那么只用一次请求即可拉取下来10个小图片的资源。减少服务器压力,减少并发,减少请求次数。

附上一个sprite的例子。

3.8 CDN

中文(内容分发网络),服务器是中心化的,CDN是“去中心化的”。

在项目中有很多东西都是放在CDN上的,比如:静态文件,音频,视频,js资源,图片。那么为什么用CDN会让资源加载变快呢?

举个简单的例子:

以前买火车票大家都只能去火车站买,后来我们买火车票就可以在楼下的火车票代售点买了。

你细品。

所以静态资源度建议放在CDN上,可以加快资源加载的速度。

3.9 懒加载

懒加载也叫延迟加载,指的是在长网页中延迟加载图像,是一种非常好的优化网页性能的方式。

当可视区域没有滚到资源需要加载的地方时候,可视区域外的资源就不会加载。

可以减少服务器负载,常适用于图片很多,页面较长的业务场景中。

如何使用懒加载呢?

3.10 iconfont

中文(字体图表),现在比较流行的一种用法。使用字体图表有几种好处

  • 矢量
  • 轻量
  • 易修改
  • 不占用图片资源请求。

就像上面说的雪碧图,如果都用字体图标来替换的画,一次请求都免了,可以直接打到bundle包中。

使用前提是UI给点力,设计趋向于字体图标,提前给好资源,建立好字体图标库。

3.11 逻辑后移

逻辑后移是一种比较常见的优化手段。用一个打开文章网站的操作来举个例子。

没有逻辑后移处理的请求顺序是这个样子的

页面的展示主体是文章展示,如果文章展示的请求靠后了,那么渲染文章出来的时间必然靠后,因为有可能因为请求阻塞等情况,影响请求响应情况,如果超过一次并发的情况的话,会更加的慢。如图的这种情况也是在我们项目中发生过的。

很明显我们应该把主体“请求文章”接口前移,把一些非主体的请求逻辑后移。这样的话可以尽快的把主体渲染出来,就会快很多。

优化后的顺序是这个样子的。

在平常的开发中建议时常注意逻辑后移的情况,突出主体逻辑。可以极大的提升用户体验。

3.12 算法复杂度

在数据量大的应用场景中,需要着重注意算法复杂度问题。

在这个方面可以参考Javascript算法之复杂度分析这篇文章。

如上面Performance解析出的Javascript执行指标上,可以推测出来你的code执行效率如何,如果执行时间过长就要考虑一下是否要优化一下复杂度了。

在时间换空间,空间换时间的选择上,要根据业务场景来进行取舍。

3.13 组件渲染

拿react举例,组件分割方面不要太深。需要控制组件的渲染,尤其是深层组件的render。

老生常谈的话题,我们可以一些方式来优化组件渲染

  • 声明周期控制 - 比如react的shouldComponentUpdate来控制组件渲染。
  • 官网提供的api- PureComponent
  • 控制注入组件的参数
  • 分配组件唯一key

没有必要的渲染是对性能的极大浪费。

3.14 node middleware

中文(node 中间件)

中间件主要是指封装所有Http请求细节处理的方法。一次Http请求通常包含很多工作,如记录日志、ip过滤、查询字符串、请求体解析、Cookie处理、权限验证、参数验证、异常处理等,但对于Web应用而言,并不希望接触到这么多细节性的处理,因此引入中间件来简化和隔离这些基础设施与业务逻辑之间的细节,让我们能够关注在业务的开发上,以达到提升开发效率的目的。

使用node middleware合并请求。减少请求次数。这种方式也是非常实用的。

3.15 web worker

Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

合理实用web worker可以优化复杂计算任务。这里直接抛阮一峰的入门文章:传送门

3.16 缓存

缓存的原理就是更快读写的存储介质+减少IO+减少CPU计算=性能优化。而性能优化的第一定律就是:优先考虑使用缓存。

缓存的主要手段有:浏览器缓存、CDN、反向代理、本地缓存、分布式缓存、数据库缓存。

3.17 GPU渲染

每个网页或多或少都涉及到一些CSS动画,通常简单的动画对于性能的影响微乎其微,然而如果涉及到稍显复杂的动画,不当的处理方式会使性能问题变得十分突出。

像Chrome, FireFox, Safari, IE9+和最新版本的Opera都支持GPU加速,当它们检测到页面中某个DOM元素应用了某些CSS规则时就会开启。

虽然我们可能不想对元素应用3D变换,可我们一样可以开启3D引擎。例如我们可以用transform: translateZ(0) 来开启GPU加速 。

只对我们需要实现动画效果的元素应用以上方法,如果仅仅为了开启硬件加速而随便乱用,那是不合理的。

3.18 Ajax可缓存

Ajax在发送的数据成功后,为了提高页面的响应速度和用户体验,会把请求的URL和返回的响应结果保存在缓存内,当下一次调用Ajax发送相同的请求(URL和参数完全相同)时,它就会直接从缓存中拿数据。

在进行Ajax请求的时候,可以选择尽量使用get方法,这样可以使用客户端的缓存,提高请求速度。

3.19 Resource Hints

Resource Hints(资源预加载)是非常好的一种性能优化方法,可以大大降低页面加载时间,给用户更加流畅的用户体验。

现代浏览器使用大量预测优化技术来预测用户行为和意图,这些技术有预连接、资源与获取、资源预渲染等。

Resource Hints 的思路有如下两个:

  • 当前将要获取资源的列表
  • 通过当前页面或应用的状态、用户历史行为或 session 预测用户行为及必需的资源

实现Resource Hints的方法有很多种,可分为基于 link 标签的 DNS-prefetch、subresource、preload、 prefetch、preconnect、prerender,和本地存储 localStorage。

3.20 SSR

渲染过程在服务器端完成,最终的渲染结果 HTML 页面通过 HTTP 协议发送给客户端,又被认为是‘同构'或‘通用',如果你的项目有大量的detail页面,相互特别频繁,建议选择服务端渲染。

服务端渲染(SSR)除了SEO还有很多时候用作首屏优化,加快首屏速度,提高用户体验。但是对服务器有要求,网络传输数据量大,占用部分服务器运算资源。

Vue的Nuxt.js和React的next.js都是服务端渲染的方法。

3.21 UNPKG

UNPKG是一个提供npm包进行CDN加速的站点,因此,可以将一些比较固定了依赖写入html模版中,从而提高网页的性能。首先,需要将这些依赖声明为external,以便webpack打包时不从node_modules中加载这些资源,配置如下:

externals: { 'react': 'React' }

其次,你需要将所依赖的资源写在html模版中,这一步需要用到html-webpack-plugin。下面是一段示例:

<% if (htmlWebpackPlugin.options.node_env === 'development') { %>
  <script src="https://unpkg.com/react@16.7.0/umd/react.development.js"></script>
<% } else { %>
  <script src="https://unpkg.com/react@16.7.0/umd/react.production.min.js"></script>
<% } %>

这段代码需要注入node_env,以便在开发的时候能够获得更友好的错误提示。也可以选择一些比较自动的库,来帮助我们完成这个过程,比如webpack-cdn-plugin,或者dynamic-cdn-webpack-plugin

3.22 域名发散与收敛

浏览器有并发限制,是为了防止DDoS攻击。

分布式拒绝服务攻击DDoS是一种基于DoS的特殊形式的拒绝服务攻击,是一种分布的、协同的大规模攻击方式。单一的DoS攻击一般是采用一对一方式的,它利用网络协议和操作系统的一些缺陷,采用欺骗和伪装的策略来进行网络攻击,使网站服务器充斥大量要求回复的信息,消耗网络带宽或系统资源,导致网络或系统不胜负荷以至于瘫痪而停止提供正常的网络服务。与DoS攻击由单台主机发起攻击相比较,分布式拒绝服务攻击DDoS是借助数百、甚至数千台被入侵后安装了攻击进程的主机同时发起的集团行为。

域名收敛:就是将静态资源放在一个域名下,减少DNS解析的开销。

域名发散:是将静态资源放在多个子域名下,就可以多线程下载,提高并行度,使客户端加载静态资源更加迅速。

域名发散是PC端为了利用浏览器的多线程并行下载能力。

而域名收敛多用与移动端,提高性能,因为dns解析是是从后向前迭代解析,如果域名过多性能会下降,增加DNS的解析开销。

手机端页面加载数和域名分散数的关系:

现在,各大浏览器都已经提升了资源的下载数,所以,域名分散的必要性也就没这么大了. 可以从上表看出,在2个域名分散条件下,网页的加载速度提升较大,而第三个以后就比较慢了。 所以, 一般来说,域名分散的数量最好在3以下。

这个方法是通过增加域名来解除并发限制,那有没有方法不用增加域名来解除并发限制呢?

SPDY。

3.23 SPDY

SPDY是google主导的一种新型通信方式.主要的特点就是多路复用. 他的目的就是致力于取消max connections 上限. 不过由于推广时间短,全世界使用的网页数都比较少. 但,据chromium统计,使用了SPDY网页速率 差不多提升了 28%~64% 左右. 所以,说SPDY的出现,可以让域名发散的存在变的毫无价值.

SPDY 协议能够完成多路复用的加密全双工通道,显著提高非wifi环境下的网络体验。

那SPDY具体的优势在哪里.

  • 解决了HTTP只能 One request per connection. 当连接完成后,可以实现并行下载多个资源文件

  • 服务器推的技术, 和SSE的理念类似,不过更靠底层。直接可以实现无需用户等待,直接后台发资源(感觉就像写APP了,有木有)

  • 请求头的复用. 当你前几次的请求头内容没多大变化的时候,就会省去几个相同的,实现Header Compression

  • 数据压缩: 在HTTP1.1 有 Content-Encoding: gzip, Transfer-Encoding: chunked. 来显式表明开启文本压缩。

  • 强制使用SSL传输协议: Google 认为 Web 将来的发展方向一定是安全的网络链接,所有请求 SSL 加密后,信息传输更加安全。

那如何开启SPDY呢? 使用nginx的用户,可以下载一个ngx_http_spdy_module 的模块. 使用apache的用户,可以下载一个 mod_spdy module 的模块.

3.24 微前端

其实微前端并不是一种性能优化方案,而是一种架构设计模式,他可以帮助开发人员将大型前端应用程序拆分成更小的,可独立部署的部分。

但是它可以间接的提高应用程序的性能。

  • 加载速度:通过将应用程序拆分成更小的部分,可以减少每个部分的代码量,从而提高应用程序的加载速度。
  • 响应速度:微前端可以帮助开发人员更好的管理应用程序的依赖关系,从而减少应用程序的复杂性和维护成本,从而提高应用程序的响应速度。

3.25 时间切片

时间切片是一种将长时间运行的任务分解成多个小任务的技术,从而避免阻塞主线程,提高页面的响应速度和用户体验。

时间切片可以使用requestIdleCallback API来实现,该API会在浏览器空闲时执行回调函数。

时间切片可以用于大量数据的计算,渲染复杂的UI组件,执行复杂的动画等场景。

使用时间切片可以提高页面的响应速度和用户体验,但是需要注意的是,时间切片也会增加代码的复杂度和维护成本,需要根据实际情况进行衡量和优化。

3.26 并发请求

如果需要同时请求多个接口,可以使用并发请求来提高页面的加载速度和用户体验。

并发请求有很多实现方案,例如axios中的all和spread:

axios.all([callback1,callback2])
    .then(axios.spread((res1, res2)=>{
 }))

我们也可以通过Promise.all()方法来实现。

并发请求也会增加网络负载和服务器负载,需要权衡使用。

3.27 SVG

svg可缩放矢量图形,顾名思义就是任意改变其大小也不会变形,是基于可扩展标记语言(XML),他严格遵从XML语法,并用文本格式的描述性语言来描述图像内容,因此是一种和图像分辨率无关的矢量图形格式,可以用记事本打开,应该更多的使用场景是手机端,因为手机分辩率差异化大。

我们的图片使用SVG也会有一定的性能提升,与栅格图形(如GIF,JPG和PNG)相比,SVG图形通常是较小的文件,页面渲染 rendering 和 painting 中都可以节省一定的时间。

3.28 HTTP2

我也思考了这个是否属于前端性能优化,最后还是将他列为了前端性能优化方案之一。

HTTP2 相比 HTTP1.1 有如下几个优点:

解析速度快

服务器解析 HTTP1.1 的请求时,必须不断地读入字节,直到遇到分隔符 CRLF 为止。而解析 HTTP2 的请求就不用这么麻烦,因为 HTTP2 是基于帧的协议,每个帧都有表示帧长度的字段。

多路复用

HTTP1.1 如果要同时发起多个请求,就得建立多个 TCP 连接,因为一个 TCP 连接同时只能处理一个 HTTP1.1 的请求。

在 HTTP2 上,多个请求可以共用一个 TCP 连接,这称为多路复用。同一个请求和响应用一个流来表示,并有唯一的流 ID 来标识。 多个请求和响应在 TCP 连接中可以乱序发送,到达目的地后再通过流 ID 重新组建。

首部压缩

HTTP2 提供了首部压缩功能。

例如有如下两个请求:

:authority: unpkg.zhimg.com
:method: GET
:path: /za-js-sdk@2.16.0/dist/zap.js
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
pragma: no-cache
referer: https://www.zhihu.com/
sec-fetch-dest: script
sec-fetch-mode: no-cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36


:authority: zz.bdstatic.com
:method: GET
:path: /linksubmit/push.js
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
pragma: no-cache
referer: https://www.zhihu.com/
sec-fetch-dest: script
sec-fetch-mode: no-cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36

从上面两个请求可以看出来,有很多数据都是重复的。如果可以把相同的首部存储起来,仅发送它们之间不同的部分,就可以节省不少的流量,加快请求的时间。

HTTP/2 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送。

下面再来看一个简化的例子,假设客户端按顺序发送如下请求首部:

Header1:foo
Header2:bar
Header3:bat

当客户端发送请求时,它会根据首部值创建一张表:

索引

首部名称

62

Header1

foo

63

Header2

bar

64

Header3

bat

如果服务器收到了请求,它会照样创建一张表。 当客户端发送下一个请求的时候,如果首部相同,它可以直接发送这样的首部块:

62 63 64

服务器会查找先前建立的表格,并把这些数字还原成索引对应的完整首部。

优先级

HTTP2 可以对比较紧急的请求设置一个较高的优先级,服务器在收到这样的请求后,可以优先处理。

流量控制

由于一个 TCP 连接流量带宽(根据客户端到服务器的网络带宽而定)是固定的,当有多个请求并发时,一个请求占的流量多,另一个请求占的流量就会少。流量控制可以对不同的流的流量进行精确控制。

服务器推送

HTTP2 新增的一个强大的新功能,就是服务器可以对一个客户端请求发送多个响应。换句话说,除了对最初请求的响应外,服务器还可以额外向客户端推送资源,而无需客户端明确地请求。

例如当浏览器请求一个网站时,除了返回 HTML 页面外,服务器还可以根据 HTML 页面中的资源的 URL,来提前推送资源。

现在有很多网站已经开始使用 HTTP2 了,例如知乎:

在这里插入图片描述

其中 h2 是指 HTTP2 协议,http/1.1 则是指 HTTP1.1 协议。

参考资料:

3.29 SSG

既然SSR属于性能优化方案,就不能不提SSG了。

SSG是一种基于原始数据和一组模板生成的完整静态的HTML网站的方法。

SSG会自动完成对单HTML个页面进行编码的任务,并且将这些页面提前准备好,以便为用户提供服务。

因为这些HTML页面是预先构建好的,所以这些页面可以很快速的加载到用户的浏览器中。

SSG有些类似于SSR(服务端渲染),区别是SSG是构建时便开始渲染了页面,而SSR是请求时渲染页面。

静态呈现意味着提前为每个URL生成单独的HTML文件, 借助预先生成的HTML响应,可以将静态渲染器部署到多个CDN,以利用边缘缓存的优势。

4.总结

对症下药才是关键,知道自己项目哪些地方需要优化,如何优化,才能更好的完善我们的项目。