前端性能优化之网络篇
这是学习后的总结和记录
用户输入url到页面呈现经历了哪些过程?
- DNS域名解析
- TCP 链接
- HTTP请求
- HTTP响应
- 渲染
但从网络层面分析, 影响性能的点有哪几部分呢?
DNS 域名解析时间太长? 可以通过浏览器DNS缓存和DNS预加载解决, TCP每次都要建立三次握手,属实没有必要怎么办呢?可以通过TCP长链接,预链接等。HTTP层面的优化可以分为 减少HTTP请求提及
和 减少HTTP请求次数
等方面优化
网络层面优化分类
# 请求过程的优化
- 构建工具Webpack调优
- 生产环境: 主要提升构建速度
- 开发环境: 主要优化打包体积
- Gzip压缩原理
- 前端图片的选型
# 减少HTTP请求次数
- 本地存储技术
- 浏览器缓存
- 离线存储技术
- CDN缓存和回源机制
Webpack构建工具调优
Webpack优化可以从开发和生产作为切入点,在开发环境下注重构建速度的提升,否则你修改一行代码command
+ s
保存,重新编译等待一分钟,阁谁谁都受不了。生产环境也就是直接面向用户的,主要注重打包的提及。你一个js包20多兆,能给用户卡死还费用户的流量。
- 缩小构建范围
- 解析loader单进程转换为多进程,使用 happypack插件
- 提前打包第三方库,通过 webpack模块的
DllPlugin
和DllReferencePlugin
创建动态链接库 - 中间件缓存步骤,通过配置 HardsourceWebpackPlugin插件
- 分包, Webpack4.x提供
optimization.splitChunks
提取公用代码 - Tree-Shaking, 删除沉淤代码
- 按需加载
以上的调优方案主要是构建工具的压缩与合并
Gzip压缩原理
开始 Gzip压缩,只需要在请求头添加一句 accept-encoding:gzip
。
HTTP压缩是一种内置到网页服务器和网页客户端中以改进传输速度和带宽利用率的方式, 在使用HTTP压缩的情况下
HTTP数据在服务器发送前就已压缩,兼容的浏览器将在下载所需的格式前宣告支持何种方法给服务器。
不支持压缩方法的浏览器将下载未经压缩的数据。最常见的压缩方案包括 Gzip 和 Deflate。
HTTP压缩就以缩小提及为目的,对HTTP内容进行重新编码的过程。
Gzip压缩的原理是在一个文本文件中找出一些重复出现的字符串,进而替换他们,从而使文件体积变小。
图片选型
不同的业务场景下使用合适的图片类型,从而达到在压缩图片体积实现优化的同时尽量保证质量
时下应用较为广泛的 Web 图片格式有JPEG/JPG、PNG、SVG、Webp以及Base64、精灵图等。
JPEG/JPG
特点: 有损压缩, 体积小, 加载快,不支持透明
应用场景: 一些网站的大图,如背景图, 轮播图和banner图等
缺点: 不支持透明,如需使用透明图则用 PNG
PNG
PNG分为 PNG-8和PNG-24。8位的PNG支持 256种颜色, 而24位的PNG支持1600万种颜色。
特点: 无损压缩, 质量高,体积大,支持透明
应用场景: 适用与更强的色彩表现力图片, 如 网站logo
,网站上的一些小图标等。
SVG
特点: 文本文件, 体积小, 不失真,兼容性好
SVG和上面的 JPG和PNG比较,具有更小的体积,可压缩性更强。
SVG使用:
- 将SVG写入HTML
- 将SVG写入独立文件后引入HTML
(使用居多)
Base64和精灵图
图像精灵(sprite,意为精灵),被运用于众多使用大量小图标的网页应用之上。它可取图像的一部分来使用,使得使用一个图像文件替代多个小文件成为可能。相较于一个小图标一个图像文件,单独一张图片所需的 HTTP 请求更少,对内存和带宽更加友好。
Base64对出现也是为了减少HTTP的请求数量, 从而提升网页性能。Base64是作为精灵图的补充而存在的。
特点: 文本文件、依赖编码、小图标解决方案
Base64是一种用于传输8Bit字节码的编码方式, 对小图标进行Base64进行编码,我们可以直接将编码的结果插入到HTML文件和Css中,从而减少HTTP请求数
Base64编码工具, 可以借助于构建工具 Webpack,使用 url-loader和file-loader进行编码
module.exports = {
// ...
module: {
rules: [
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000 // 小于 10000
}
}
]
}
}
以上代码配置小于10000字节的图片都将为Base64编码。
Webp
WebP是Google开发的一种无损压缩图像格式。 它支持有损压缩和无损压缩,可以说它集成了 JPG、PNG等优点。 唯一缺点就是兼容性不好。
浏览器缓存技术
缓存可以减少网络 IO 消耗,提高访问速度。浏览器缓存是一种操作简单、效果显著的前端性能优化手段。
通过网络获取内容既速度缓慢又开销巨大。较大的响应需要在客户端与服务器之间进行多次往返通信,这会延迟浏览器获得和处理内容的时间,还会增加访问者的流量费用。因此,缓存并重复利用之前获取的资源的能力成为性能优化的一个关键方面。
按照获取资源时请求的优先级排序:
- Memory Cache(内存缓存)
- Service Worker Cache(服务工作缓存)
- HTTP Cache(HTTP缓存)
- Push Cache(推高速缓存)
HTTP Cache 浏览器缓存
HTTP缓存可以分为 强缓存 和 协商缓存
强缓存
强缓存是利用 HTTP头部的 Expires
和 Cache-Control
两个字段来控制的。当请求再次发送时,浏览器会根据其中的 Expires
和Cache-Control
来判断是否命中强缓存,如果命中则直接返回缓存中的资源,不会和服务器发生通讯。
强缓存的标记: form dist cache
Expires
和Cache-Control
有什么关系呢?
Cache-Control
是 Expires
的替代品。之前都是使用 Expires,在响应头中返回 Expires强缓存字段值。类型如下:
expires: Wed, 11 Sep 2019 16:12:18 GMT
Expires是一个时间戳,当我们再次向服务器发送请求,浏览器会先对比 expires时间戳和客户端时间,如果小于客户端时间,说明缓存没有过期,直接去缓存中取资源。
但是 Expires有一个缺陷, 对比依赖客户端时间。如果服务器和客户端时间不一致,我们手动修改客户端时间,那么 expires是无法达到我们的预期。
HTTP1.1
新增了 Cache-Control
来替代 Expires。它和Expires的作用相同, Expires能做的事情它都可以做, Expires不能做的事情它还可以做。
Cache-Control字段如下cache-control: max-age=31536000
Cache-Control是通过 max-age
来控制缓存资源的有效期, 它并不是时间戳,而是一个时间长度。表示该资源在多少秒内是有效的。
Cache-Control比Expires更加准确, 它的优先级更高。当 Cache-Contorl和Expires同时存在时,会优先使用 Cache-Control。
no-cache 绕开了浏览器: 当我们为资源设置了 no-cache时,每一次发送的请求都不会去询问浏览器的缓存情况, 而是直接去服务端确认该资源是否过期。
no-store表示不使用任何缓存,也不会去服务端确认资源是否过期,只允许你向服务端发送请求,并下载完整的响应
协商缓存
协商缓存依赖于客户端与服务端的通讯。
客户端会向服务端询问缓存的相关信息,进而判断是重新发送请求,下载完整的资源。还是从本地获取缓存的资源。
如果服务端资源未曾改动(last-Modified),资源会被重定向到浏览器缓存,这种情况下网络请求的状态码为 304
Last-Modified是一个时间戳, 第一次请求时会随着响应头返回。Last-Modified: Fri, 27 Oct 2017 06:35:57 GMT
当我们再次请求时, 请求头会携带一个 If-Modified-Since
资源,其对应的时间戳就是 Last-Modified返回的时间戳。If-Modified-Since: Fri, 27 Oct 2017 06:35:57 GMT
当服务端拿到请求中的 If-Modified-Since, 会对比该时间戳和资源在服务器上最后修改的时间是否一只,从而判断资源是否改动。如果发生了变化就返回一个完整的响应内容, 并且更新响应头(respones headers)中的 Last-Modified为最新值。 否则就返回304响应, 且响应头不会携带 Last-Modified字段。
Last-Modified字段存在一些缺点, 有时服务器无法感知文件是否发生变化。为了解决这以问题, Etag来弥补 Last-Modified的不足。
Etag是服务器为每个资源生成的唯一的 标示字符串, 生成的方式是根据文件的内容进行编码的,当文件内容改变时,Etag的值也会发生变化,反之依然。所以Etag可以精确的感知文件的变动。
Etag和Last-Modified一样,首次请求时,响应头里获取一个最初的标示字符串。如下:ETag: W/"2a3b-1602480f459"
当下一次的请求时, 请求头中会携带相同的、名为 if-None-Match
的字符串和服务器的标示对比。更精确的判断文件是否改动。
If-None-Match: W/"2a3b-1602480f459"
Etag在感知文件的变动比Last-Modified更精确,所以Etag的优先级更高。Etag和Last-Modified同时存在,以 Etag为准。
MemoryCache(内存缓存)
MemoryCache,是指存在内存中的缓存。从优先级上来说,它是浏览器最先尝试去命中的一种缓存。从效率上来说,它是响应速度最快的一种缓存。
内存缓存是快的,也是短命的。它和渲染进程生死相依。当进程结束后,内存缓存的数据也将不复存在。
内存缓存资源存放的位置具有一定的随机性。
Service Worker Cache(服务工作缓存)
Service Worker 是一种独立于主线程之外的 Javascript 线程。它脱离于浏览器窗体,因此无法直接访问 DOM。这样独立的个性使得 Service Worker 的“个人行为”无法干扰页面的性能,这个“幕后工作者”可以帮我们实现离线缓存、消息推送和网络代理等功能。我们借助 Service worker 实现的离线缓存就称为 Service Worker Cache。
Service Worker 的生命周期包括 install、active、working 三个阶段。一旦 Service Worker 被 install,它将始终存在,只会在 active 与 working 之间切换,除非我们主动终止它。这是它可以用来实现离线存储的重要先决条件。
Push Cache
Push Cache 是指 HTTP2 在 server push 阶段存在的缓存。这块的知识比较新,应用也还处于萌芽阶段。
存储技术
HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie使基于无状态的HTTP协议记录稳定的状态信息成为了可能。
由于Cookie的大小有限,根据不同的浏览器大小各不相同。最大能存储 5kb。一般多应用于:
- 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
- 个性化设置(如用户自定义设置、主题等)
- 浏览器行为跟踪(如跟踪分析用户行为等)
如果让Cookie存储一些大量的数据显然是不合理的,因为Cookie每次都会携带在请求头部发送给服务器,所以也会造成HTTP请求头过大。
HTML5 Web Storage
Web Storage是HTML5为了解决客户端存储而诞生的。允许你在一个特定的域中设置、检索和删除数据和存储类型。
Web Storage分为:
sessionStorage: 给每一个给定的源,维持一个独立的存储区域,该存储区域在页面回话期间可用,也就是当浏览器关闭了,这个保存的数据也随之销毁。
const data = [1,2,3]
sessionStorage.setItem('arr', data) // 存储 arr为键名, data是要存储的数据
let getData = sessionStorage.getItem('arr') // 根据键名去对应的数据
console.log(typeof getData) // string // 取出的是字符串类型
当关闭浏览器再重新开启这个页面, 存储的数据会不复存在,因为它是 会话存储。
localStorage: 和 sessionStorage一样, 只不过它是持久化存储,除非你手动删除存储的数据,否则关闭浏览器后数据仍然存在。
const data = [1,2,3]
localStorage.setItem('arr', data) // 存储 arr为键名, data是要存储的数据
let getData = localStorage.getItem('arr') // 根据键名去对应的数据
console.log(typeof getData) // string // 取出的是字符串类型
localStorage.removeItem('arr') // 根据指定的键名清楚缓存
当你存储完数据之后, 关闭浏览器再打开数据还是存在的。因为它是 持久存储
localStorage和sessionStorage存储的数据都不会被请求头所携带, 而Cookie是每次都会被请求头携带
CDN的缓存和回源机制
CDN(内容分发网络
) 是将源站内容分发至最接近用户的节点, 使用户可就近取得所需的内容,提高用户访问的响应的速度和成功率。解决因分布、带宽、服务器性能带来的访问延迟问题。
CND是如何工作的:
假设我的根服务器在杭州, 同时北京、上海、成都、山东都有我的机房。 此时身在山东的你向我请求资源,在网络带宽小、用户访问量大的情况下,杭州的根服务器不能给用户非常快的响应速度。那么我可以把这些资源复制一份放到距离用户最近的机房呀。当你请求资源时,山东这台服务器低头一看,这个资源我存了,况且离得这么近响应速度那是杠杠的。如果山东这台服务器没有存你要的资源,它就会向杭州的根服务器要资源。此刻, 山东的这台服务器就扮演着 CND
的角色。
CDN的两个核心点就是 缓存
和回源
。缓存就相当于把根服务器的资源复制一份放到山东的机房中。而回源就是 山东机房发现没有你要的资源,它就向根服务器或者上级服务器去要资源。
根服务器也就是业务服务器,需要对业务进行大量的计算。而CDN服务器可以理解为一个 仓库
,只负责存放和转运一些东西,特别是静态的资源。如 js、css、image等。
总结
前端性能优化网络篇我们分别从HTTP请求优化
和 减少HTTP请求次数
入手。实践了 webpack调优、Gzip压缩、前端图片选型、浏览器缓存、本地存储、CDN的缓存等几方面入手。