如何正确压缩 webpack 块?

Posted

技术标签:

【中文标题】如何正确压缩 webpack 块?【英文标题】:How to properly compress webpack chunks? 【发布时间】:2019-12-24 23:21:30 【问题描述】:

我已经在构建一个工作网络应用,其配置已停用 gzip 压缩。

我使用 htmlWebpackPlugin 将 Vue 内容和生成的块(js 和 css)注入到主 index.html

块是由 Webpack 4 的 splitChunks 指令创建的。

我现在想要的是激活CompressionWebpackPlugin 以使我的大输出文件更小。

如果没有块,压缩插件会做得很好。它为每个块创建一个*.js.gz 文件,但是它不会将每个块中的引用更新到其他块。

因此,浏览器将无法找到所有需要的文件。

例如index.html 有一个或两个对chunk1.js.gzchunk2.js.gz 的引用,但在chunk1chunk2 内部有许多对其他块的引用,这些引用无法找到,因为引用的结尾是.js 和真实文件以.js.gz结尾。

如何正确配置CompressionWebpackPlugin(或任何其他整个 webpack 设置和插件),以便它们相应地更新文件引用?

这怎么可能是完全动态的,以至于我什至可以使用最小比率或最小文件大小的压缩功能? (那么一些输出块会是.js,一些会是.js.gz,所以一些引用必须是第一个选项,一些引用必须是第二个选项。

类似问题的其他答案说,应该压缩服务器端而不是使用 webpack。但是,那压缩插件是出于什么原因存在的呢?

设置是Vue、Webpack(单页应用)、nginx、Django、Docker。

这是我当前的 Webpack 配置:

'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

const env = process.env.NODE_ENV === 'testing'
    ? require('../config/test.env')
    : require('../config/prod.env')

const webpackConfig = merge(baseWebpackConfig, 
    mode: 'production',
    module: 
        rules: utils.styleLoaders(
            sourceMap: config.build.productionSourceMap,
            extract: true,
            usePostCSS: true
        )
    ,
    devtool: config.build.productionSourceMap ? config.build.devtool : false,
    output: 
        path: config.build.assetsRoot,
        filename: utils.assetsPath('js/[name].[chunkhash].js'),
        chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
    ,
    plugins: [
        // extract css into its own file
        new MiniCssExtractPlugin(
            filename: utils.assetsPath('css/[name].[hash:7].css'),
            chunkFilename: utils.assetsPath('css/[id].[hash:7].css')
        ),
        // Compress extracted CSS. We are using this plugin so that possible
        // duplicated CSS from different components can be deduped.
        new OptimizeCSSPlugin(
            cssProcessorOptions: config.build.productionSourceMap
            ?  safe: true, map:  inline: false  
            :  safe: true 
        ),
        // generate dist index.html with correct asset hash for caching.
        // you can customize output by editing /index.html
        // see https://github.com/ampedandwired/html-webpack-plugin
        new HtmlWebpackPlugin(
            filename: process.env.NODE_ENV === 'testing'
                ? 'index.html'
                : config.build.index,
            template: 'index.html',
            inject: true,
            minify: 
                removeComments: true,
                collapseWhitespace: true,
                removeAttributeQuotes: true
                // more options:
                // https://github.com/kangax/html-minifier#options-quick-reference
            ,
            // necessary to consistently work with multiple chunks via CommonsChunkPlugin
            chunksSortMode: 'dependency',
            jsExtension: '.gz'
        ),
        // keep module.id stable when vender modules does not change
        new webpack.HashedModuleIdsPlugin(),
        // copy custom static assets
        new CopyWebpackPlugin([
            
                from: path.resolve(__dirname, '../static'),
                to: config.build.assetsSubDirectory,
                ignore: ['.*']
            
        ]),
    ],
    optimization: 
        runtimeChunk: 'single',
        splitChunks: 
            chunks: 'all',
            maxInitialRequests: Infinity,
            minSize: 0,
            cacheGroups: 
                default: false,
                vendors: false,
                vendor: 
                    test: /[\\/]node_modules[\\/]/,
                    chunks: 'all',
                    priority: 20,
                    name(module) 
                        // get the name. E.g. node_modules/packageName/not/this/part.js
                        // or node_modules/packageName
                        const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];

                        // npm package names are URL-safe, but some servers don't like @ symbols
                        return `npm.$packageName.replace('@', '')`;
                    ,
                ,
                common: 
                    name: 'common',
                    minChunks: 2,
                    chunks: 'async',
                    priority: 10,
                    reuseExistingChunk: true,
                    enforce: true
                ,
            ,
        ,
    ,
)

if (config.build.productionGzip) 
    const CompressionWebpackPlugin = require('compression-webpack-plugin')

    webpackConfig.plugins.push(
        new CompressionWebpackPlugin(
            filename: '[path].gz[query]',
            algorithm: 'gzip',
            test: /\.(js|css)(\?.*)?$/i,
            //threshold: 10240,
            //minRatio: 0.8,
            deleteOriginalAssets: true,
        ),
    )


if (config.build.bundleAnalyzerReport) 
    const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
    webpackConfig.plugins.push(new BundleAnalyzerPlugin())


module.exports = webpackConfig

【问题讨论】:

【参考方案1】:

关于为什么存在压缩插件的具体问题,答案是它可以在您的服务器端创建压缩包。

javascript 的请求是从带有普通 JS 扩展的客户端浏览器发出的。但是,浏览器请求中发送的标头包括它可以接受的响应类型。查看 accept-encoding 字段,通常您会在那里看到 gzip 和 Brotli。

您的服务器应该会收到对 JS 文件的请求,但是那里的代码应该在标头中查找以找出作为响应的格式。您的服务器通常应该返回之前使用客户端可以处理的压缩插件压缩的内容。

【讨论】:

以上是关于如何正确压缩 webpack 块?的主要内容,如果未能解决你的问题,请参考以下文章

webpack 如何压缩文件

webpack 如何压缩文件

webpack打包js代码

如何在 Webpack 中生成动态导入块名称

使用 webpack 代码拆分,如何加载块和 HTML 布局?

前端自动化构建工具Webpack开发模式入门指南