HTTP 概述

GET 请求包含一个 URL 和 HTTP 响应(状态码、响应头、响应体)。如果浏览器和服务器支持,可以使用压缩来减少响应的大小。浏览器可以使用 Accept-Encoding 头来声明它支持压缩,服务器使用 Content-Encoding 头来确认响应已被压缩。

浏览器会自动对资源进行缓存,如果浏览器不确定缓存是否有效,就是生成 GET 请求使用 If-Modified-Since 头将最后修改的日期(最后修改日期基于响应中 Last-Modeified 头参数)发给服务器,如果资源没有变化服务器会返回 304 Not Modified 状态码不再发送响应体。

Expires 头通过制定资源的缓存有效时间消除浏览器缓存有效性的效验。

Connection: Keep-Alive 开启持久连接,通过 Connection: Close 头来关闭。

CDN

CDN 的原理是尽可能的在各个地方分布机房缓存数据,这样即使我们的根服务器远在国外,在国内的用户也可以通过国内的机房迅速加载资源。

因此,我们可以将静态资源尽量使用 CDN 加载,由于浏览器对于单个域名有并发请求上限,可以考虑使用多个 CDN 域名。并且对于 CDN 加载静态资源需要注意 CDN 域名要与主站不同,否则每次请求都会带上主站的 Cookie,平白消耗流量。

缓存

通常浏览器缓存策略分为两种:强缓存协商缓存,并且缓存策略都是通过设置 HTTP Header 来实现的。

强缓存

强缓存可以通过设置两种 HTTP Header 实现:ExpiresCache-Control 。强缓存表示在缓存期间不需要请求,state code 为 200。

Expires

  1. Expires: Wed, 22 Oct 2018 08:41:00 GMT

Expires 是 HTTP/1 的产物,表示资源会在 Wed, 22 Oct 2018 08:41:00 GMT 后过期,需要再次请求。并且 Expires 受限于本地时间,如果修改了本地时间,可能会造成缓存失效。

Cache-control

  1. Cache-control: max-age=30

Cache-Control 出现于 HTTP/1.1,优先级高于 Expires 。该属性值表示资源会在 30 秒后过期,需要再次请求。
Cache-Control 可以在请求头或者响应头中设置,并且可以组合使用多种指令

前端性能优化(一)减少 HTTP 请求 - 图1
多种指令配合流程图

从图中我们可以看到,我们可以将多个指令配合起来一起使用,达到多个目的。比如说我们希望资源能被缓存下来,并且是客户端和代理服务器都能缓存,还能设置缓存失效时间等等。

接下来我们就来学习一些常见指令的作用
前端性能优化(一)减少 HTTP 请求 - 图2常见指令作用

协商缓存

如果缓存过期了,就需要发起请求验证资源是否有更新。协商缓存可以通过设置两种 HTTP Header 实现:Last-ModifiedETag

当浏览器发起请求验证资源时,如果资源没有做改变,那么服务端就会返回 304 状态码,并且更新浏览器缓存有效期。
前端性能优化(一)减少 HTTP 请求 - 图3
协商缓存

Last-Modified 和 If-Modified-Since

Last-Modified 表示本地文件最后修改日期,If-Modified-Since 会将 Last-Modified 的值发送给服务器,询问服务器在该日期后资源是否有更新,有更新的话就会将新的资源发送回来,否则返回 304 状态码。

但是 Last-Modified 存在一些弊端:

  • 如果本地打开缓存文件,即使没有对文件进行修改,但还是会造成 Last-Modified 被修改,服务端不能命中缓存导致发送相同的资源
  • 因为 Last-Modified 只能以秒计时,如果在不可感知的时间内修改完成文件,那么服务端会认为资源还是命中了,不会返回正确的资源

因为以上这些弊端,所以在 HTTP / 1.1 出现了 ETag

ETag 和 If-None-Match

ETag 类似于文件指纹,If-None-Match 会将当前 ETag 发送给服务器,询问该资源 ETag 是否变动,有变动的话就将新的资源发送回来。并且 ETag 优先级比 Last-Modified 高。

以上就是缓存策略的所有内容了,看到这里,不知道你是否存在这样一个疑问。如果什么缓存策略都没设置,那么浏览器会怎么处理?
**
对于这种情况,浏览器会采用一个启发式的算法,通常会取响应头中的 Date 减去 Last-Modified 值的 10% 作为缓存时间。

实际场景应用缓存策略

单纯了解理论而不付诸于实践是没有意义的,接下来我们来通过几个场景学习下如何使用这些理论。

频繁变动的资源

对于频繁变动的资源,首先需要使用 Cache-Control: no-cache 使浏览器每次都请求服务器,然后配合 ETag 或者 Last-Modified 来验证资源是否有效。这样的做法虽然不能节省请求数量,但是能显著减少响应数据大小。

代码文件

这里特指除了 HTML 外的代码文件,因为 HTML 文件一般不缓存或者缓存时间很短。

一般来说,现在都会使用工具来打包代码,那么我们就可以对文件名进行哈希处理,只有当代码修改后才会生成新的文件名。基于此,我们就可以给代码文件设置缓存有效期一年 Cache-Control: max-age=31536000,这样只有当 HTML 文件中引入的文件名发生了改变才会去下载最新的代码文件,否则就一直使用缓存。

Service Worker

Service Worker 的缓存与浏览器其他内建的缓存机制不同,它可以让我们自由控制缓存哪些文件、如何匹配缓存、如何读取缓存,并且缓存是持续性的

当 Service Worker 没有命中缓存的时候,我们需要去调用 fetch 函数获取数据。也就是说,如果我们没有在 Service Worker 命中缓存的话,会根据缓存查找优先级去查找数据。但是不管我们是从 Memory Cache 中还是从网络请求中获取的数据,浏览器都会显示我们是从 Service Worker 中获取的内容。

CSS 精灵(CSS Sprites)

将多张图片合并在一张图中,使用 CSS 的 background-position 属性将 HTML 元素移到期望的图片上。

优点

  • 减少图片的字节
  • 减少了网页的 HTTP 请求,从而大大的提高了页面的性能
  • 解决了网页设计师在图片命名上的困扰,只需对一张集合的图片上命名就可以了,不需要对每一个小元素进行命名,从而提高了网页的制作效率。
  • 更换风格方便,只需要在一张或少张图片上修改图片的颜色或样式,整个网页的风格就可以改变。维护起来更加方便。

内联图片

使用 Base64 字符串来代替图片的 URL 属性,如下所示,以 data:image/png;base64 开头。

  1. <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAAAwCAYAAACVMr0DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyMThFN0FFOTYzNDAxMUU0Qjg0MEY0MTgyQzZENjY3MSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyMThFN0FFQTYzNDAxMUU0Qjg0MEY0MTgyQzZENjY3MSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjIxOEU3QUU3NjM0MDExRTRCODQwRjQxODJDNkQ2NjcxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjIxOEU3QUU4NjM0MDExRTRCODQwRjQxODJDNkQ2NjcxIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+pUgM2QAABbxJREFUeNrkW01sG0UUnqQrBWHamEqt1FKIkZBSCWH7UA49lDicuAC+U6mGA1LFoemhF6gU9wASJ3IA5YS6UYMEJxLgwImu20MOjYQdCSmHStj8RWpRsd0fEakonRfeVpPdt7Mz3tl4J33Sar2zv/727fe+92ZmZGtri9lqK7lima/yw3yGk/fXPNn+EdsA5qACoHW+1PgynoFH6vFlCZ6Jg922GmAObpWv3IwAS9m7HGRXbBi1CNwKX31rGtxc8Tg7cOpVU5e7zJ+zZp0HIy20TYILwE5+M8fGXji6vf1f/x67+f5Fduf7n0xc/kWfLmzx4BnTniuCC7bvwDNs8mveNnHUxOXrUg9+6cjhKv4p0dybG7dc4ZgCBhrf2uJ+4prwiVfENn58XdGDwRsmTIELlPDyj1+S+/74ZJ79/vG8ids8y72460jeQInwItEA4Flhu4EBSPZWp6LedAw9TDD7DCSk5xCeVibA7fDlZ77P356WeKonANngXlqJOLSh8aBG7cHa+jbnAi2ENNe1VaP3Go3gu6DNGXqjorWH5VoPe3fZrxc+DbXf/uo71r9+w+i9HIInzxBCGj79zwa9CfJ1MEgVeDtFEVIuN2W3F5fZZucvNv7aie3tf/lvaDNtjoKnzvE/3BXoYRCjaGKK4GQVLieDlg/UoPYUVw/Pf3RW6xyVYOgIXuYS3NtSjfQDAGzMANxjH57ddapRAXhUAJeihmrSh+DXzpu4jq3mcABmIsCtcO8dJBDlie2oIFkLSDBtesg8wAQgLfjjHNym5DwKsDJ/Wc0AzbTxJdUlyYcIsGcqwIEiuHVlORMAi14LAa0OlMGXM5LzShQVEu1NBFHM3jyJtxuzzc6fUslFBcY0lISDevQ8psJdbK8SXl3SvLYv73YUaTjg0wLIoReyW55FBcb+9VXzAAc/SaxDBDXrkgbALQSqjvKuGSHHKOvuuSCnmMm5CeRaOwAwUIaXRTDGNLQwpNQqWZ9DeO8UIfzzWGPQtSaREheEmkemPBjKl+p6el4PYNSrboRiyGt85nGKoxAV4GKUi70UgeB6jC5qJ/WqNpEiU8WfoRtU2O631pWOBcWhmmiU0XNLMcGnMQhFgFcG6xh4zwJBRUM1APeXN94z7sHVOIWAn26SekIjQDFllqHy5W6rCFldQSfQzQic2iSUxBMHcAfXsu4ZnUAnBjF4MeeEbUpre3sVYA9rAB56aBr9Xx6RVjNJCm3EZJ2b9PEnYChU7HEruaJWJpe652BGJ0u3l9ketaTjIi5x8EZgUTh2acB9T06QS2gA4uywAd78Tb1iNjbxHDv0zlt2AIx6mKKJBaGKZ8yiut+ho1N1YAlweFKAUx06BRkiX2oxSiGx9z5dPB7Oynp39wQHy6yK2raGYBfYzqFWvtUT89z4/nBWtrZuF8BYr9CpU5wLyDE3otZRihgfoSWvRLvzw1UrVQSVKjcVX85MTJIyi6VSbTv45uuhNkNDUHddRQQB6Chq6L9ZeFTQAq7Ffj/oB6zoliyPfHB6J/f277F/JADvy+9XGnDt8OOOEcV3uL5xgCOGVKmMV7vAl4uEavB5WRxoCPQB2WRZdbjAodNvh+gBBlE/lAS43CuTWtld0q9DlSKCkR5G/MgAhg5PGIG5GuDdhg+uQDutQApdUH34wxxg0aCrPm162Ph8MRWKEEFqMXnpEkqTVUyPK5TnBlLoCsq3qDRaiTIAXPDeoFSDnmITBl8FJCi66kQ30XgMXoTX1iM8OwSuCDL7f9CKS9AQFF+6K7kiVPrIIpRsbgWAYbqArmFNHYBl4PkWxZ3nY87zga5hFz9lLpVmb3yxyPrXbkg5d0i2AI4BP5RmGYEGTiOdVbU0ZhmlbI9nGVkzERHnyV214FF3TEa0ZiIizgmeRrrKovWYzTM9BZBBxl3KENA9TJzKQXCtoogI2hj2bPsuB1UqI60G2AZ7JMAA5DJcXbfGMfsAAAAASUVORK5CYII=">

为何要使用 Base64 编码图片

  • 使用 Base64 编码可以减少网络请求
  • 将图片转为字符串后,图片文件会随着 HTML 元素一并加载,这样就可以减少 HTTP 请求的次数
  • 采用 Base64 编码的图片是随着页面一起加载的,不会造成跨域请求问题
  • 没有清理图片缓存的问题

Base64 图片的弊端

  • Base64 编码后生成的字符串大小会增加约 30%,增大 HTML/CSS 体积,而且可读性差
  • IE8 不兼容,由于 IE8 以下不支持 data url 格式

使用场景

  • 图片的尺寸很小,且很少被更新
  • 存在跨域限制的地方
  • 不想页面缓存的图片

合并脚本和样式

合并脚本和样式会减少 HTTP 请求的次数。但是将它们合并在一起,会造成代码不清晰。在上线部署时,将同一个页面的脚本和样式进行合并。

压缩资源

通过 HTTP 请求中的 Accpt-Encoding 头表示对压缩的支持。

压缩什么:压缩脚本、样式和 HTML 文档。
节省:数据传输内容减少。

代理压缩

向 Web 服务器响应中添加 Vary: Accept-Encoding 。当浏览器带着 Accpt-Encoding:gzip 访问代理时,它接受到的是压缩过的内容。没有 Accpt-Encoding 头的浏览器收到的就是未经压缩的内容。

压缩前端打包代码

  1. $ npm install compression-webpack-plugin -D

然后在 vue.config.js 中加入

  1. const CompressionWebpackPlugin = require("compression-webpack-plugin");
  2. const productionGzipExtensions = /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i;
  3. module.exports = {
  4. configureWebpack: config => {
  5. const plugins = [];
  6. // Begin 生成 gzip 压缩文件
  7. plugins.push(
  8. new CompressionWebpackPlugin({
  9. filename: "[path].gz[query]",
  10. algorithm: "gzip",
  11. test: productionGzipExtensions,
  12. threshold: 10240,
  13. minRatio: 0.8
  14. })
  15. );
  16. // End 生成 gzip 压缩文件
  17. config.plugins = [...config.plugins, ...plugins];
  18. }
  19. }

Nginx

  1. $ vim /usr/local/nginx/conf/nginx.conf
  1. gzip on; # 开启Gzip
  2. gzip_min_length 1k; # 不压缩临界值,大于1K的才压缩,一般不用改
  3. gzip_buffers 4 16k; # 默认就好
  4. #gzip_http_version 1.0; # 默认就好
  5. gzip_comp_level 2; # 压缩级别,1-10,数字越大压缩的越好,时间也越长,根据自己的需求更改
  6. gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; # 进行压缩的文件类型,缺少什么类型加上,JavaScript有两种写法都写上.
  7. gzip_vary off; # Squid等缓存服务
  8. gzip_disable "MSIE [1-6]\."; # IE6对Gzip不友好,忽略IE6

重新加载 nginx

  1. $ sudo /usr/local/nginx/sbin/nginx -s reload

查看相应页面的 DevTools 是否有响应头 Content-Encoding: gzip,如果有则代表 Gzip 成功。

参考

【1】高性能网站建设指南:前端工程师技能精髓
【2】前端面试之道
【3】Nginx开启Gzip压缩大幅提高页面加载速度