如何为项目开启gzip压缩及实现原理

2021-04-19  本文已影响0人  AizawaSayo

项目的线上版本我们一般都会结合构建工具(webpack)插件和服务端配置(nginx)来实现 http 传输 的 gzip 压缩,目的就是把服务端响应的文件的体积尽量减小,优化返回速度。那这件事具体是怎么实现的呢?

http 传输中 gzip 压缩的原理

客户端(浏览器)在请求静态资源(js、css等)的时候,在请求头 Request Header 里带上 accept-encoding字段来表明接受哪些压缩方法, 如accept-encoding: gzip, deflate;服务端在接收到请求时如果发现有这个配置,则发送gzip压缩版本的文件,并在响应头 Response Headers 配置一个 content-encoding 字段,用于说明服务端数据的压缩方法(可选值是gzip、compress、deflate),否则(没有accept-encoding)就发送源文件;客户端再根据返回响应头里 content-encoding 对应的格式去做相应的解码/解压缩;没有content-encoding项则不进行解压缩。
accept-encodingcontent-encoding字段都非常语义化,就是传输的文件以怎样的格式编码/解码,所以当响应头有content-encoding: gzip出现,就能说明我们服务端的gzip压缩成功开启了。如下:

同时我们还能看到压缩后的文件 size,记得先在Chrome浏览器的 Network 下勾选 ☑️Use large request rows

谁来压缩文件?

(1) 服务端响应请求时压缩
如果我们在服务器用Nginx代理部署项目,就直接让 nginx 来处理压缩,它有专门为此构建的内容,可以更好地利用缓存并减少开销。我们要做的只是在服务端的 nginx.conf做好配置,其他就不需要我们操心了。

配置参数可参考:Nginx的gzip配置文档

    # 开启gzip压缩
    gzip on;
    gzip_buffers 4 16k; # 置用于压缩响应的缓冲区的数量和大小
    gzip_comp_level 9;  # 对响应压缩的级别,可选范围:1到9,数字越大压缩得越好,但也越占用CPU时间
    gzip_http_version 1.1; # 默认 1.1,请求压缩响应所需的最小HTTP版本
    gzip_min_length  1k;  # 设置被gzip的响应的最小长度,小于该值的文件不会被压缩
    # 追加启用gzip压缩的MIME类型,默认已有text/html
    gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json;
gzip_disable "MSIE [1-6]\.";
    gzip_vary on; # 默认off,如果指令gzip、gzip_static或gunzip是active的,启用插入" Vary: Accept-Encoding "响应报头字段

具体配置位置展示

(2) 项目打包构建生产版本时压缩
既然服务端都可以做压缩,为什么在 webpack 打包应用时还要多此一举呢?我们可以看上面 nginx 配置中 gzip_comp_level 这个配置项,数字越大压缩效果越好,但是会耗费更多的CPU和时间,我们压缩文件主要是为了减少传输时间,如果每次请求静态资源服务端都要压缩很久才会返回信息,不仅本末倒置吗?况且服务器开销也会增大很多。既然现在的 spa 应用文件都是打包生成的,我们在打包的时候就直接生成高压缩等级的文件,作为静态资源放在服务器上,接收到请求后直接把这些压缩版文件返回回去不就好了?

webpack 的 compression-webpack-plugin 就是专门做这件事情的:

tip:我bulid的时候报了Cannot read property 'tapPromise' of undefined的错,其实就是版本和vue-cli的某些包不兼容,把 compression-webpack-plugin 的版本降低到6.1.1就可以了。

先安装npm install compression-webpack-plugin -D,然后到vue.config.js配置:

const CompressionPlugin = require("compression-webpack-plugin");

configureWebpack: config => {
    config.name = name
    const plugins = []
    if (IS_PROD) { // 生产环境
      plugins.push( 
        // 为静态资源准备压缩版本,在服务器也要开启相应配置
        new CompressionWebpackPlugin({
          test: /\.(js|css|json|ico|svg)$/,// 匹配文件格式
          algorithm: 'gzip',
          threshold: 10240, // 对超过10k的数据压缩
          minRatio: 0.8, // 压缩比
          // filename: "[path][base].gz", // 压缩后的文件名,默认值是 [path][base].gz
          filename(pathData) {
            // `pathData` 参数包含很多可以获取到文件路径相关数据的属性 - `path`/`name`/`ext`/等等
            // 如果路径中包含svg,则放到svg/目录下
            // 只是演示,一般都用字符串默认值就好
            if (/\.svg$/.test(pathData.ext)) {
              return 'static/svg/[base].gz'
            }
            return '[path][base].gz'
          },
          deleteOriginalAssets: false, // 不删除源文件,true 则只保留压缩后的文件
        })
      )
    } else {
      // 为开发环境修改配置
    }
    config.plugins = [...config.plugins, ...plugins]
  },

其中 filename 参数就是定义文件编码压缩后的路径和文件名,格式除了字符串也可以是Function(基本没啥必要)。
默认值是"[path][base].gz",一般保持默认值就好。
gz是文件后缀,那[path][base] 是啥呢

比如我们有这么个静态资源:static/images/image.png?foo=bar#hash (static是我打包目录下自定义的静态资源目录),然后若我们给插件 filename 的值里配置如下参数,完成压缩后输出的文件名中:

在filename给svg单独配文件名的效果 打完包大部分文件会多出一个gz版本, 根据我们的配置,size小于threshold数值的文件不会生成gz版本

参考文章:探索HTTP传输中gzip压缩

上一篇 下一篇

猜你喜欢

热点阅读