Vuejs的Webpack压缩插件未进行压缩

8

我需要帮助调试 Webpack 的 Compression Plugin 。

问题概述

  • 目标是启用资源压缩并减小应用程序的包大小。使用Brotli算法作为默认值,并对不支持该算法的浏览器使用gzip以备降级。
  • 我预期在资源的响应头中会有一个content-encoding字段,但实际上它们没有这个字段。我使用Chrome开发者工具的网络选项卡来确认这一点。更多细节请见下面的代码片段: example asset request
  • 本地运行时,我的浏览器或IDE没有显示错误信息。

我尝试过的解决方案

  • 尝试使用不同的压缩插件实现。请参见以下方法列表:
    1. (使用Webpack Chain API)
config
 .plugin('brotliCompress')
     .use(CompressionWebpackPlugin, [{
       exclude: /.map$/,
       cache: true,
       algorithm: 'brotliCompress',
       test: /\.(js|css|html|svg)$/,
       threshold: 10240,
       minRatio: 0.8,
     }])

(使用Webpack Chain API)
config
  .plugin('gzip')
      .use(CompressionWebpackPlugin, [{
        algorithm: 'gzip',
        test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
        threshold: 8192, // Assets larger than 8192 bytes are not processed
        minRatio: 0.8, // Assets compressing worse that this ratio are not processed
      }])
  1. (使用Webpack Chain API)
config
  .plugin('CompressionPlugin')
      .use(CompressionWebpackPlugin)
  1. (使用vue-cli-plugin: compression)当我用vue invoke compression代替使用Webpack Chain API进行压缩配置,并在运行vue add compression后,响应IDE控制台消息时,出现生成器丢失错误。
  pluginOptions: {
    compression: {
      brotli: {
        filename: '[file].br[query]',
        algorithm: 'brotliCompress',
        include: /\.(js|css|html|svg|json)(\?.*)?$/i,
        minRatio: 0.8,
      },
      gzip: {
        filename: '[file].gz[query]',
        algorithm: 'gzip',
        include: /\.(js|css|html|svg|json)(\?.*)?$/i,
        minRatio: 0.8
      }
    }
  },
  1. 最后,我尝试将阈值字段设置为0,并将其提高到大于10k字节。

重点说明

  • 上述尝试并没有实现我在第一个总结符号中所述的目标,而是用来代替之前测试的方法。
  • 我优先考虑了Webpack Chain API,因为它在重新构建和运行应用程序时没有产生任何错误。

参考链接/文档

代码

vue.config.js

const path = require('path')
const CompressionWebpackPlugin = require('compression-webpack-plugin')

function resolve (dir) {
  return path.join(__dirname, dir)
}

module.exports = {
  /* ....shortened for brevity */

  // Compress option VI (with vue cli plugin, generator bug when invoked)
  // pluginOptions: {
  //   compression: {
  //     brotli: {
  //       filename: '[file].br[query]',
  //       algorithm: 'brotliCompress',
  //       include: /\.(js|css|html|svg|json)(\?.*)?$/i,
  //       minRatio: 0.8,
  //     },
  //     gzip: {
  //       filename: '[file].gz[query]',
  //       algorithm: 'gzip',
  //       include: /\.(js|css|html|svg|json)(\?.*)?$/i,
  //       minRatio: 0.8
  //     }
  //   }
  // },

  chainWebpack: config => {
    config
      .resolve.alias
        .set('@', resolve('src'))

    config
      .plugins.delete('prefetch') 
        
    config
      .optimization.splitChunks()

    config
      .output
      .chunkFilename('[id].js')

    // The below configurations are recommeneded only in prod.
    // config.when(process.env.NODE_ENV === 'production', config => { config... })

    // Compress option VII
    // config
      // .plugin('gzip')
      // .use(CompressionWebpackPlugin, [{
      //   algorithm: 'gzip',
      //   test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
      //   threshold: 8192, // Assets larger than 8192 bytes are not processed
      //   minRatio: 0.8, // Assets compressing worse that this ratio are not processed
      // }])

    // Compress option VIII
    // config
      // .plugin('CompressionPlugin')
      // .use(CompressionWebpackPlugin)

    config
      .plugin('brotliCompress')
      .use(CompressionWebpackPlugin, [{
        exclude: /.map$/,
        // deleteOriginalAssets: true,
        cache: true,
        algorithm: 'brotliCompress',
        test: /\.(js|css|html|svg)$/,
        threshold: 10240,
        minRatio: 0.8,
      }])
  },
}

package.json

"dependencies": {
    "@auth0/auth0-spa-js": "^1.15.0",
    "audio-recorder-polyfill": "^0.4.1",
    "compression-webpack-plugin": "^6.0.0",
    "core-js": "^3.6.5",
    "dotenv": "^8.2.0",
    "dotenv-expand": "^5.1.0",
    "moment": "^2.29.1",
    "register-service-worker": "^1.7.1",
    "uuid": "^3.4.0",
    "vue": "^2.6.11",
    "vue-loader": "^15.9.8",
    "vue-router": "^3.5.1",
    "vuex": "^3.6.2"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-plugin-pwa": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "vue-cli-plugin-compression": "~1.1.5",
    "vue-template-compiler": "^2.6.11",
    "webpack": "^4.46.0"
  }

我感谢所有的反馈。谢谢。

3个回答

5
看起来 compression-webpack-plugin 只是压缩文件,但它不会自动配置 dev server 以便在原始文件的位置提供经过压缩的文件。
然而,你可以通过 vue.config.jsdevServer 选项(传递到 webpack-dev-server)手动设置中间件来实现这一点:
  1. 将所有接受 br 编码的 .js 请求重写为将 .br 追加到原始 URL,与传递给 compression-webpack-pluginfilename 设置匹配。这有效地获取了插件压缩的 .br 文件。
  2. 设置响应标头以指示 br 内容编码application/javascript 内容类型,以便浏览器能够理解如何处理文件。

Vue CLI 5 (Webpack 5)

使用 devServer.setupMiddlewares
// vue.config.js
const CompressionPlugin = require('compression-webpack-plugin')

module.exports = {
  transpileDependencies: true,
  configureWebpack: {
    plugins: [
      new CompressionPlugin({   1️⃣
        filename: '[path][base].br',
        algorithm: 'brotliCompress',
        test: /\.js$/,
      })
    ]
  },
  devServer: {
    setupMiddlewares(middlewares, devServer) {
      if (!devServer) {
        throw new Error('webpack-dev-server is not defined')
      }

      middlewares.unshift({
        name: 'serve-brotli-js',
        path: '*.js',
        middleware: (req, res, next) => {
          if (req.get('Accept-Encoding')?.includes('br')) {
            1️⃣
            req.url += '.br'

            2️⃣
            res.set('Content-Encoding', 'br')
            res.set('Content-Type', 'application/javascript; charset=utf-8')
          }
          next()
        }
      })

      return middlewares
    }
  }
}

Vue CLI 4 (Webpack 4)

使用devServer.before

注:与Webpack 5唯一的区别是Express的app直接作为参数传递给了devserver.before()

// vue.config.jsmodule.exports = {
  ⋮
  devServer: {
    before(app) {
      // same code as Webpack 5 above
    }
  }
}

GitHub演示


1
毫无疑问,这正是我所期待的回复,谢谢Tony。我将参考共享的代码示例和文档,以增加我对webpack的了解。感谢您提供的解决方案! - Dan der Mensch

0

在您的nginx.conf文件中添加以下内容,如果客户端支持gzip解析,则只要服务器能够返回gzip,就可以启用gzip。

gzip on; #  Turn on Gzip
gzip_static on; #  Turn on static file compression 
gzip_min_length  1k; #  Incompressible critical value , Greater than 1K It s only a matter of time 
gzip_buffers     4 16k;
gzip_comp_level 5;
gzip_types     application/javascript application/x-javascript application/xml application/xml+rss application/x-httpd-php text/plain text/javascript text/css image/jpeg image/gif image/png; #  The type of file to be compressed 
gzip_http_version 1.1;
gzip_vary on;
gzip_proxied   expired no-cache no-store private auth;
gzip_disable   "MSIE [1-6]\.";

0

目前尚不清楚哪个服务器正在提供这些资源。如果是Express,查看带有头部X-Powered-By的屏幕截图,https://github.com/expressjs/compression/issues/71显示Express尚未添加Brotli支持。

但是,可能有一种方法可以手动指定content-encoding标头。


你的回复让我学到了一些东西@wegry,在进一步研究webpack如何提供应用程序后,我发现它确实使用了express。不幸的是,即使使用了第二种方法(使用gzip算法),问题仍然存在,而且它并没有达到最终目标。您能否添加更多关于手动指定内容编码头以及如何触发压缩的上下文?我很重视您的回复。 - Dan der Mensch
@DanderMensch,tony19的回答详细介绍了服务器如何与浏览器通信以处理Brotli编码。主要点是在生产Vue构建中,服务器将不得不确定内容编码。 - wegry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接