美文网首页
如何为项目开启gzip压缩及实现原理

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

作者: AizawaSayo | 来源:发表于2021-04-19 18:01 被阅读0次

    项目的线上版本我们一般都会结合构建工具(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 的值里配置如下参数,完成压缩后输出的文件名中:

    • [path] 会被替换为源静态资源的目录, 包括末尾的 / (static/images/)
    • [file] 为源静态资源的路径 (static/images/image.png)
    • [name] 为源文件的文件名 (image)
    • [ext] 为源文件(包含.)的扩展名 (.png)
    • [base] 会被替换为 ([name] + [ext]) 的内容 (image.png)
    • [query] 为源文件的查询参数,包括 ? (?foo=bar)
    • [fragment] 为源文件的 fragment (在URL的概念里叫做hash)(#hash)
    在filename给svg单独配文件名的效果 打完包大部分文件会多出一个gz版本, 根据我们的配置,size小于threshold数值的文件不会生成gz版本

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

    相关文章

      网友评论

          本文标题:如何为项目开启gzip压缩及实现原理

          本文链接:https://www.haomeiwen.com/subject/usfvlltx.html