反应-生产中的webpack sass / scss模块

时间:2019-11-29 22:51:17

标签: reactjs webpack sass

我有适用于我的开发环境的scss模块,但是当我部署到生产环境时,我的样式没有被编译/应用。如何配置webpack,以使开发环境和生产环境保持一致?我知道webpack文件非常混乱,但是如果有人指出我做错了什么,我将非常感激。

webpack.base.js

const path = require('path')
const Dotenv = require('dotenv-webpack')
const HtmlWebPackPlugin = require('html-webpack-plugin')

module.exports = options => {
    let envPath = '.env'
    process.argv.forEach(val => {
        if (val.includes('--env=')) {
            const curEnv = val.slice(6)
            if (['dev', 'stg'].includes(curEnv)) {
                envPath = `.env.${curEnv}`
            }
        }
    })
    return {
        mode: options.mode,
        devServer: options.devServer,
        entry: [path.join(process.cwd(), 'src/main.js')],
        output: {
            path: path.join(__dirname, '../dist/'),
            publicPath: '/',
            filename: '[name].[hash].js',
            chunkFilename: '[id].[hash].js'
        },
        resolve: {
            extensions: ['.js', '.jsx'],
            modules: [
                path.join(__dirname, '../src'),
                path.join(__dirname, '../node_modules')
            ],
            alias: {
                '@constants': path.join(__dirname, '../src/constants'),
                '@c': path.join(__dirname, '../src/components'),
                '@C': path.join(__dirname, '../src/containers')
            }
        },
        plugins: options.plugins.concat([
            new Dotenv({
                path: path.join(process.cwd(), envPath)
            }),
            new HtmlWebPackPlugin({
                inject: true,
                template: path.join(__dirname, '../src/static/index.html'),
                favicon: path.join(__dirname, '../src/static/favicon.ico')
            })
        ]),
        module: {
            rules: options.module.rules.concat([
                // {
                //  enforce: 'pre',
                //  test: /\.jsx?$/,
                //  exclude: /node_modules/,
                //  use: [
                //      {
                //          loader: 'eslint-loader',
                //          options: {
                //              quiet: true
                //          }
                //      }
                //  ]
                // },
                {
                    test: /\.(woff|woff2|eot|ttf|otf)$/,
                    exclude: [ /\.scss$/ ],
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                name: '[hash].[ext]',
                                outputPath: 'fonts'
                            }
                        }
                    ]
                },
                {
                    test: /\.svg$/,
                    loader: 'svg-react-loader'
                },
                {
                    test: /\.(gif|png|jpe?g)$/i,
                    use: [
                        {
                            loader: 'url-loader',
                            options: {
                                limit: 10 * 1024,
                                name: '[hash].[ext]',
                                outputPath: 'assets'
                            }
                        },
                        {
                            loader: 'image-webpack-loader',
                            options: {
                                disable: options.mode === 'development'
                            }
                        }
                    ]
                }
            ])
        }
    }
}

webpack.dev.js

const webpack = require('webpack')
const BrowserSyncPlugin = require('browser-sync-webpack-plugin')

module.exports = require('./webpack.base')({
    mode: 'development',
    devServer: {
        hot: true,
        port: 3000,
        historyApiFallback: true
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new BrowserSyncPlugin(
            { proxy: 'http://localhost:3000/', open: false },
            { reload: false }
        )
    ],
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        plugins: [
                            [
                                'react-css-modules',
                                {
                                    "filetypes": {
                                        ".scss": { "syntax": "postcss-scss" }
                                    },
                                    "generateScopedName": '[name]_[local]_[hash:base64:5]'
                                }
                            ],
                        ],
                    },
                },
                resolve: {
                        extensions: ['.js', '.jsx']
                }
            },
            {
                test: /\.(sa|sc)ss$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true,
                            sourceMap: true,
                            localIdentName: '[name]_[local]_[hash:base64:5]'
                        }
                    },
                    {
                        loader: 'sass-loader'
                    }
                ]
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader', 'postcss-loader']
            },
        ]
    }
})

webpack.prod.js

const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');

module.exports = require('./webpack.base')({
    mode: 'production',
    devServer: {
        port: 3000,
        contentBase: path.join(process.cwd(), 'dist/')
    },
    optimization: {
        minimize: true,
        minimizer: [
            new TerserPlugin({
                cache: true,
                parallel: true
            }),
            new OptimizeCSSAssetsPlugin()
        ]
    },
    module: {
        rules: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules\/@babel/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [
                            [
                            '@babel/preset-env',
                                {
                                    'targets': {
                                        'ie': '11'
                                    },
                                    'loose': true,
                                    'forceAllTransforms': true
                                }
                            ],
                            '@babel/preset-react'
                        ],
                        sourceType: 'unambiguous',
                        plugins: [
                            [
                                '@babel/plugin-proposal-decorators',
                                {
                                    'legacy': true
                                }
                            ],
                            [
                                'react-css-modules',
                                {
                                    "filetypes": {
                                        ".scss": { "syntax": "postcss-scss" }
                                    },
                                    "generateScopedName": '[name]_[local]_[hash:base64:5]'
                                }
                            ],
                            '@babel/plugin-transform-runtime',
                            '@babel/plugin-syntax-dynamic-import',
                            '@babel/plugin-proposal-function-bind',
                            '@babel/plugin-proposal-class-properties',
                            '@babel/plugin-proposal-export-default-from',
                            '@babel/plugin-proposal-export-namespace-from'
                        ]
                    }
                },
                resolve: {
                    extensions: ['.js', '.jsx']
                }
            },
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader',
                    'sass-loader'
                ]
            },
            {
                test: /\.(sa|sc)ss$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true,
                            sourceMap: false,
                            localIdentName: '[name]_[local]_[hash:base64:5]'
                        }
                    },
                    {
                        loader: 'sass-loader'
                    }
                ]
            },
        ]
    },
    plugins: [
        new CleanWebpackPlugin([path.join(process.cwd(), '/dist')], {
            allowExternal: true
        }),
        new MiniCssExtractPlugin({
            filename: '[name].[hash].css',
            chunkFilename: '[id].[hash].css'
        }),
        new CompressionPlugin({
            test: /\.(js|css)$/,
            filename: asset => asset.file
        })
    ]
})

webpack.profile.js

const path = require('path')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CompressionPlugin = require('compression-webpack-plugin')
const VisualizerPlugin = require('webpack-visualizer-plugin')

module.exports = require('./webpack.base')({
    mode: 'production',
    devServer: {
        port: 3000,
        contentBase: path.join(process.cwd(), 'dist/')
    },
    optimization: {
        minimizer: [
            new UglifyJsPlugin({
                cache: true,
                parallel: true
            }),
            new OptimizeCSSAssetsPlugin()
        ]
    },
    module: {
        rules: [
            {
                test: /\.(sa|sc|c)ss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'postcss-loader',
                    'sass-loader'
                ]
            }
        ]
    },
    plugins: [
        new CompressionPlugin({
            test: /\.(js|css|html)$/
        }),
        new VisualizerPlugin({
            filename: '../stats/bundleStats.html'
        }),
        new MiniCssExtractPlugin({
            filename: '[name].[hash].css',
            chunkFilename: '[id].[hash].css'
        })
    ]
})

package.json

  "scripts": {
    "start": "webpack-dev-server --config webpack/webpack.dev.js --env=dev",
    "start:stg": "webpack-dev-server --config webpack/webpack.dev.js --env=stg",
    "start:prod": "webpack-dev-server --config webpack/webpack.dev.js --env=prod",
    "build:dev": "webpack --config webpack/webpack.prod.js --env=dev",
    "build:stg": "webpack --config webpack/webpack.prod.js --env=stg",
    "build:prod": "webpack --config webpack/webpack.prod.js --env=prod",
    "deploy:dev": "node scripts/deploy.js --env=dev",
    "deploy:stg": "node scripts/deploy.js --env=stg",
    "deploy:prod": "node scripts/deploy.js --env=prod",
    "profile": "webpack --config webpack/webpack.profile.js --progress --profile --colors",
    "profile:stats": "webpack --config webpack/webpack.profile.js --profile --json > ./stats/webpack-build-stats.json",
    "formatsvg": "svgo -f ./src/images/icons --enable=inlineStyles,removeAttrs  --config '{ \"plugins\": [ { \"inlineStyles\": { \"onlyMatchedOnce\": false } }, { \"removeAttrs\": { \"attrs\": \"filter\" } } ] }' --pretty",
    "format": "prettier --write \"**/*.js\" prettier --write \"**/*.jsx\"",
    "eslint": "eslint --quiet ./src"
  },

1 个答案:

答案 0 :(得分:1)

对于您的webpack.prod.js文件,您需要更改此rule

            {
                test: /\.(sa|sc)ss$/,
                use: [
                    { loader: 'style-loader' },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: true,
                            sourceMap: false,
                            localIdentName: '[name]_[local]_[hash:base64:5]'
                        }
                    },
                    {
                        loader: 'sass-loader'
                    }
                ]
            },

对此:

           {
             test: /\.(sc|sa)ss$/,
             use: [
               { loader: MiniCssExtractPlugin.loader },
               {
                 loader: "css-loader",
                 options: {
                   modules: true,
                   sourceMap: false,
                   localIdentName: "[name]_[local]_[hash:base64:5]"
                 }
               },
               {  loader: "sass-loader" }
             ]
           },

本质上,您需要在开发中使用style-loader进行热重装,而在生产中,则需要使用MiniCssExtractPlugin将所有内容编译到一个或多个CSS文件中。


您可以选择几种实现方式...

您可以简单地根据环境要求并合并所需的配置(推荐用于当前设置-您只需导出对象并进行合并):

const merge = require('webpack-merge');
const base = require('./config/webpack.base.js');

const envs = {
  development: 'dev',
  production: 'prod',
};

const env = envs[process.env.NODE_ENV || 'development'];
const config = require(`./config/webpack.${env}.js`);

module.exports = merge(base, config);

或者...如果要将不同的配置简化为一个配置,并根据环境有条件地使用加载程序,则可以执行以下操作:

const { NODE_ENV } = process.env;
const inDevelopment = NODE_ENV === "development";

const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;

const cssRule = ({ exclude, modules, sourceMap, test }) => ({
    test,
    exclude,
    use: [
        inDevelopment ? "style-loader" : MiniCssExtractPlugin.loader,
        {
            loader: "css-loader",
            options: {
                sourceMap: sourceMap || inDevelopment,
                modules: !!modules,
                localIdentName: "[name]_[local]_[hash:base64:5]"
            },
        },
        "sass-loader",
    ],
}); 

module.exports = {
  mode: inDevelopment ? "development" : "production",
  devServer: {
    port: 3000,
    contentBase: path.join(process.cwd(), 'dist/')
  },
  entry: [path.join(process.cwd(), 'src/main.js')],

  ... include other configurations

  module: {
        rules: [
            // css imports
            cssRule({ test: cssRegex, exclude: cssModuleRegex }),
            // css module imports
            cssRule({ test: cssModuleRegex, modules: true }),
            // scss imports
            cssRule({ test: sassRegex, exclude: sassModuleRegex }),
            // scss module imports
            cssRule({ test: sassModuleRegex, modules: true }),
            ...etc
        ]
    },
 ... etc
}