Webpack CSS输出始终是压缩的。

9
针对我特定的项目,我需要控制CSS的缩小并仅缩小某些文件。我已经非常接近一个可行的解决方案,使用OptimizeCSSAssetsPlugin,在其中使用assetNameRegExp选项来选择要缩小的CSS文件。
我已经花了一段时间尝试弄清楚为什么所有其他的 CSS 文件仍然被缩小。原来,sass-loader在生产模式下始终会缩小你的 CSS 文件,无论你是否想要这样做。
这是我的完整webpack.config.js
const CopyWebpackPlugin = require('copy-webpack-plugin');
const FixStyleOnlyEntriesPlugin = require('webpack-fix-style-only-entries');
const FractalWebpackPlugin = require('fractal-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const path = require('path');
const PrettierPlugin = require('prettier-webpack-plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = (env, argv) => {
    /**
     * Determine if is production mode from the command executed
     */
    const isProduction = argv.mode === 'production';

    /**
     * Common paths
     */
    const paths = {
        src: 'src',
        dev: 'public',
        prod: 'public'
    };

    /**
     * Generate the settings for webpack depending on if it is
     * development or production mode.
     */
    const settings = {
        mode: isProduction ? 'production' : 'development',
        outputDir: isProduction ? paths.prod : paths.dev,
        fractal: {
            mode: isProduction ? 'build' : 'server',
            sync: isProduction ? false : true
        }
    };

    return {
        // Mode is set by --mode property in command
        mode: settings.mode,

        /**
         * 3 entries:
         *      designSystem: This is Design System UI specific CSS
         *      website: This is website & component specific CSS
         *      app: This is the website & component specific JS
         */
        entry: {
            /**
             * Main website and Design System CSS files
             */
            designSystem: path.resolve(__dirname, `./${paths.src}/theme/scss/theme.scss`),
            website: path.resolve(__dirname, `./${paths.src}/scss/styles.scss`),

            /**
             * Specific enteries for all comonents to generate a CSS file specific to that component
             */
            headings: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/02-headings/headings.scss`),
            paragraphs: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/03-paragraphs/paragraphs.scss`),
            inlineElements: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/04-inline-elements/inline-elements.scss`),
            ordered: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/05-lists/ordered/ordered.scss`),
            unordered: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/05-lists/unordered/unordered.scss`),
            images: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/06-images/images.scss`),
            spacers: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/01-layout/02-spacers/spacers.scss`),
            primaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/primary-button/primary-button.scss`),
            secondaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/secondary-button/secondary-button.scss`),
            tertiaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/tertiary-button/tertiary-button.scss`),
            checkboxes: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/checkboxes/checkboxes.scss`),
            inputs: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/inputs/inputs.scss`),
            labels: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/labels/labels.scss`),
            radios: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/radios/radios.scss`),
            selects: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/selects/selects.scss`),
            textareas: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/textareas/textareas.scss`),
            footer: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/04-footer/footer.scss`),
            navigation: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/05-navigation/navigation.scss`),
            informationPanel: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/06-information-panel/information-panel.scss`),
            informationPill: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/07-information-pill/information-pill.scss`),
            modal: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/08-modal/modal.scss`),

            /**
             * Main website and Design System JS files
             */
            app: [
                'regenerator-runtime/runtime',
                'core-js/modules/es6.array.from',
                'core-js/modules/es6.array.for-each',
                'core-js/modules/es6.object.assign',
                'core-js/modules/es6.promise',
                path.resolve(__dirname, `./${paths.src}/js/app.js`)
            ]
        },

        /**
         * JS output goes into the scripts folder and depending on mode will
         * either go into the public or the dist folder with it's chunks
         */
        output: {
            path: path.resolve(__dirname, `./${settings.outputDir}`),
            filename: 'scripts/[name].js',
            chunkFilename: 'scripts/[name].chunk.js'
        },

        module: {
            rules: [
                {
                    parser: {
                        amd: false
                    }
                },
                {
                    /**
                     * Load JS files with Babel Loader and set to transpile code to work
                     * in IE10 and above.
                     */
                    test: /\.(js)$/,
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: 'babel-loader',
                            options: {
                                configFile: './babel.config.js',
                                presets: [
                                    [
                                        '@babel/preset-env',
                                        {
                                            useBuiltIns: 'entry',
                                            corejs: '^3.1.4',
                                            targets: {
                                                browsers: ['defaults, ie >= 10']
                                            }
                                        }
                                    ]
                                ]
                            }
                        },
                        {
                            loader: 'eslint-loader',
                            options: {
                                configFile: '.eslintrc.json'
                            }
                        }
                    ]
                },
                {
                    /**
                     * Load SASS files with 2 loaders
                     *      PostCSS: This converts the SCSS to CSS, adds in polyfills for flexbox,
                     *               auto prefixes and adds in normalise CSS.
                     *      SASS Loader: This generates source maps for CSS.
                     */
                    test: /\.(scss|sass)$/,
                    use: [
                        {
                            loader: MiniCssExtractPlugin.loader
                        },
                        {
                            loader: 'css-loader',
                            options: {
                                sourceMap: true
                            }
                        },
                        {
                            loader: 'postcss-loader',
                            options: {
                                plugins: () => [
                                    require('postcss-flexbugs-fixes'),
                                    require('postcss-preset-env')({
                                        autoprefixer: {
                                            flexbox: 'no-2009'
                                        },
                                        stage: 3
                                    }),
                                    require('autoprefixer')()
                                ],
                                sourceMap: true,
                                minimize: false
                            }
                        },
                        {
                            loader: 'sass-loader',
                            options: {
                                sourceMap: true,
                                minimize: false,
                                outputStyle: 'uncompressed'
                            }
                        }
                    ]
                },
                {
                    /**
                     * This looks for all images and uses the File Loader to move them to
                     * the output directory. It excludes the fonts directory so there is no
                     * duplication of SVG files
                     */
                    test: /\.(png|jpg|jpeg|gif|svg)$/,
                    exclude: /fonts/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                name: '[folder]/[name].[ext]',
                                outputPath: '/images'
                            }
                        }
                    ]
                },
                {
                    /**
                     * This looks for all font files and uses the File Loader to
                     * move hem to the output directory. It excludes the images directory
                     * so there is no duplication of SVG files
                     */
                    test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
                    exclude: /images/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                name: '[folder]/[name].[ext]',
                                outputPath: '/fonts'
                            }
                        }
                    ]
                }
            ]
        },

        plugins: [
            /**
             * This prevents webpack from generating a JS file for SCSS entries
             */
            new FixStyleOnlyEntriesPlugin(),
            /**
             * Runs SASS linting
             */
            new StyleLintPlugin({
                configFile: '.stylelintrc.json',
                context: 'src',
                files: '**/*.scss',
                failOnError: false,
                quiet: false,
                emitErrors: true
            }),
            /**
             * This outputs SCSS entires into CSS files and thier chunks
             */
            new MiniCssExtractPlugin({
                filename: 'style/[name].css',
                chunkFilename: 'style/[name].chunk.css'
            }),
            /**
             * Runs Fractal in either server mode for dev and build mode for
             * production.
             */
            new FractalWebpackPlugin({
                mode: settings.fractal.mode,
                sync: settings.fractal.sync
            }),
            /**
             * Copies images over to the output directory
             */
            new CopyWebpackPlugin([
                {
                    from: path.resolve(__dirname, `./${paths.src}/images`),
                    to: 'images'
                }
            ]),
            // new PrettierPlugin()
        ],

        /**
         * This only runs when in production mode and will minify JS and CSS
         */
        optimization: {
            minimize: true,
            minimizer: [
                new OptimizeCSSAssetsPlugin({
                    assetNameRegExp: /style\/(website|designSystem).css/,
                    cssProcessor: require('cssnano')
                }),
                new TerserPlugin({
                    include: /\/js/,
                    exclude: /\/scss/
                })
            ]
        },

        /**
         * Generates source maps
         */
        devtool: 'source-maps'
    };
};
1个回答

16

我最终解决了我的问题,并想发布答案,这样如果将来有人遇到这个问题,他们就可以解决它。

尽管我的问题描述中的内容不同,但一开始实际上我并不知道哪个loader导致了问题,在做了一些研究后,我最初认为css-loader是罪魁祸首。我深入挖掘代码后发现css-loader没有进行缩小处理。接下来要研究的下一个loader是sass-loader,经过大量研究,我最终发现sass-loader在进行缩小处理。在研究了sass-loader的文档后,我似乎找不到任何关于缩小处理或如何停止它的信息。经过大量搜索,我最终找到了非常差劲的文档选项outputStyle

从我所找到的内容来看,outputStyle有3个选项:

outputStyle: 'compressed'
outputStyle: 'uncompressed'
outputStyle: 'expanded'

这是我的一个技巧,sass-loader似乎不会注意到webpack.config.js中的minimize:false选项,但它将听取outputStyle选项。这将关闭所有CSS文件的缩小功能。然后,这允许OptimizeCSSAssetsPlugin发挥作用并压缩你需要的文件。

下面是新的sass-loader代码:

{
    loader: 'sass-loader',
    options: {
        sourceMap: true,
        minimize: false,
        outputStyle: 'expanded'
    }
}

这是完整的 webpack.config.js 文件。

const CopyWebpackPlugin = require('copy-webpack-plugin');
const FixStyleOnlyEntriesPlugin = require('webpack-fix-style-only-entries');
const FractalWebpackPlugin = require('fractal-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const path = require('path');
const PrettierPlugin = require('prettier-webpack-plugin');
const StyleLintPlugin = require('stylelint-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = (env, argv) => {
    /**
     * Determine if is production mode from the command executed
     */
    const isProduction = argv.mode === 'production';

    /**
     * Common paths
     */
    const paths = {
        src: 'src',
        dev: 'public',
        prod: 'public'
    };

    /**
     * Generate the settings for webpack depending on if it is
     * development or production mode.
     */
    const settings = {
        mode: isProduction ? 'production' : 'development',
        outputDir: isProduction ? paths.prod : paths.dev,
        fractal: {
            mode: isProduction ? 'build' : 'server',
            sync: isProduction ? false : true
        }
    };

    return {
        // Mode is set by --mode property in command
        mode: settings.mode,

        /**
         * 3 entries:
         *      designSystem: This is Design System UI specific CSS
         *      website: This is website & component specific CSS
         *      app: This is the website & component specific JS
         */
        entry: {
            /**
             * Main website and Design System CSS files
             */
            designSystem: path.resolve(__dirname, `./${paths.src}/theme/scss/theme.scss`),
            website: path.resolve(__dirname, `./${paths.src}/scss/styles.scss`),

            /**
             * Specific enteries for all comonents to generate a CSS file specific to that component
             */
            headings: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/02-headings/headings.scss`),
            paragraphs: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/03-paragraphs/paragraphs.scss`),
            inlineElements: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/04-inline-elements/inline-elements.scss`),
            ordered: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/05-lists/ordered/ordered.scss`),
            unordered: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/03-typography/05-lists/unordered/unordered.scss`),
            images: path.resolve(__dirname, `./${paths.src}/patterns/01-branding/06-images/images.scss`),
            spacers: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/01-layout/02-spacers/spacers.scss`),
            primaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/primary-button/primary-button.scss`),
            secondaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/secondary-button/secondary-button.scss`),
            tertiaryButton: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/02-buttons/tertiary-button/tertiary-button.scss`),
            checkboxes: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/checkboxes/checkboxes.scss`),
            inputs: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/inputs/inputs.scss`),
            labels: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/labels/labels.scss`),
            radios: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/radios/radios.scss`),
            selects: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/selects/selects.scss`),
            textareas: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/03-form-elements/textareas/textareas.scss`),
            footer: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/04-footer/footer.scss`),
            navigation: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/05-navigation/navigation.scss`),
            informationPanel: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/06-information-panel/information-panel.scss`),
            informationPill: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/07-information-pill/information-pill.scss`),
            modal: path.resolve(__dirname, `./${paths.src}/patterns/02-ui-components/08-modal/modal.scss`),

            /**
             * Main website and Design System JS files
             */
            app: [
                'regenerator-runtime/runtime',
                'core-js/modules/es6.array.from',
                'core-js/modules/es6.array.for-each',
                'core-js/modules/es6.object.assign',
                'core-js/modules/es6.promise',
                path.resolve(__dirname, `./${paths.src}/js/app.js`)
            ]
        },

        /**
         * JS output goes into the scripts folder and depending on mode will
         * either go into the public or the dist folder with it's chunks
         */
        output: {
            path: path.resolve(__dirname, `./${settings.outputDir}`),
            filename: 'scripts/[name].js',
            chunkFilename: 'scripts/[name].chunk.js'
        },

        module: {
            rules: [
                {
                    parser: {
                        amd: false
                    }
                },
                {
                    /**
                     * Load JS files with Babel Loader and set to transpile code to work
                     * in IE10 and above.
                     */
                    test: /\.(js)$/,
                    exclude: /node_modules/,
                    use: [
                        {
                            loader: 'babel-loader',
                            options: {
                                configFile: './babel.config.js',
                                presets: [
                                    [
                                        '@babel/preset-env',
                                        {
                                            useBuiltIns: 'entry',
                                            corejs: '^3.1.4',
                                            targets: {
                                                browsers: ['defaults, ie >= 10']
                                            }
                                        }
                                    ]
                                ]
                            }
                        },
                        {
                            loader: 'eslint-loader',
                            options: {
                                configFile: '.eslintrc.json'
                            }
                        }
                    ]
                },
                {
                    /**
                     * Load SASS files with 2 loaders
                     *      PostCSS: This converts the SCSS to CSS, adds in polyfills for flexbox,
                     *               auto prefixes and adds in normalise CSS.
                     *      SASS Loader: This generates source maps for CSS.
                     */
                    test: /\.(scss|sass)$/,
                    use: [
                        {
                            loader: MiniCssExtractPlugin.loader
                        },
                        {
                            loader: 'css-loader',
                            options: {
                                sourceMap: true
                            }
                        },
                        {
                            loader: 'postcss-loader',
                            options: {
                                plugins: () => [
                                    require('postcss-flexbugs-fixes'),
                                    require('postcss-preset-env')({
                                        autoprefixer: {
                                            flexbox: 'no-2009'
                                        },
                                        stage: 3
                                    }),
                                    require('autoprefixer')()
                                ],
                                sourceMap: true,
                                minimize: false
                            }
                        },
                        {
                            loader: 'sass-loader',
                            options: {
                                sourceMap: true,
                                minimize: false,
                                outputStyle: 'expanded'
                            }
                        }
                    ]
                },
                {
                    /**
                     * This looks for all images and uses the File Loader to move them to
                     * the output directory. It excludes the fonts directory so there is no
                     * duplication of SVG files
                     */
                    test: /\.(png|jpg|jpeg|gif|svg)$/,
                    exclude: /fonts/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                name: '[folder]/[name].[ext]',
                                outputPath: '/images'
                            }
                        }
                    ]
                },
                {
                    /**
                     * This looks for all font files and uses the File Loader to
                     * move hem to the output directory. It excludes the images directory
                     * so there is no duplication of SVG files
                     */
                    test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
                    exclude: /images/,
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                name: '[folder]/[name].[ext]',
                                outputPath: '/fonts'
                            }
                        }
                    ]
                }
            ]
        },

        plugins: [
            /**
             * This prevents webpack from generating a JS file for SCSS entries
             */
            new FixStyleOnlyEntriesPlugin(),
            /**
             * Runs SASS linting
             */
            new StyleLintPlugin({
                configFile: '.stylelintrc.json',
                context: 'src',
                files: '**/*.scss',
                failOnError: false,
                quiet: false,
                emitErrors: true
            }),
            /**
             * This outputs SCSS entires into CSS files and thier chunks
             */
            new MiniCssExtractPlugin({
                filename: 'style/[name].css',
                chunkFilename: 'style/[name].chunk.css'
            }),
            /**
             * Runs Fractal in either server mode for dev and build mode for
             * production.
             */
            new FractalWebpackPlugin({
                mode: settings.fractal.mode,
                sync: settings.fractal.sync
            }),
            /**
             * Copies images over to the output directory
             */
            new CopyWebpackPlugin([
                {
                    from: path.resolve(__dirname, `./${paths.src}/images`),
                    to: 'images'
                }
            ]),
            // new PrettierPlugin()
        ],

        /**
         * This only runs when in production mode and will minify JS and CSS
         */
        optimization: {
            minimize: true,
            minimizer: [
                new OptimizeCSSAssetsPlugin({
                    assetNameRegExp: /style\/(website|designSystem).css/,
                    cssProcessor: require('cssnano')
                }),
                new TerserPlugin({
                    include: /\/js/,
                    exclude: /\/scss/
                })
            ]
        },

        /**
         * Generates source maps
         */
        devtool: 'source-maps'
    };
};

3
这对我解决了问题,但我必须在选项中使用“sassOptions”属性:options: { sourceMap: true, sassOptions: { minimize: false, outputStyle: 'expanded' } }。 - Galivan
谢谢,这对我很有帮助。我需要设置'mode: "development"'。 - 00-BBB
sassOptions 对我来说也是必要的。这可能是由于较新的 Webpack 版本引起的。 - Michael Heumann

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