using mini-css-extract-plugin and style-loader together
Asked Answered
B

3

13

I am new to that webpack thing and following some tutorials to learn basics.

I would like to use style-loader to inject stylesheets during development (with HMR enabled) and want to use MiniCssExtractPlugin for production builds. But when I use MiniCssExtractPlugin plugin, I loose injecting feature of style-loader.

Please see my webpack config :

const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');

module.exports = {
    entry: ['./src/index.js'],
    output: {
        filename: 'app.js',
        path: path.resolve(__dirname, 'dist')
    },
    module: {
        rules: [
            {
                test: /\.(js|jsx)$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader"
                }
            },
            {
                test: /\.(sass|scss)$/,
                use: [
                    "style-loader",
                   {
                        loader: MiniCssExtractPlugin.loader,
                        options: {
                            hmr: process.env.NODE_ENV === 'development'
                        }
                    },
                    "css-loader",
                    "sass-loader"
                ]
            }
        ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[id].css'
        })
    ],
    devServer: {
        contentBase: path.join(__dirname, 'dist'),
        compress: true,
        hot: true,
        port: 3000
    }
};
Bedridden answered 14/4, 2019 at 17:42 Comment(1)
"But when I use MiniCssExtractPlugin plugin, I loose injecting feature of style-loader" - what do you mean? in the previous sentence you mentioned that you want style-loader in dev mode & MiniCssExtractPlugin in prod mode. Your requirement is not clear. Kindly clarifyEllette
Q
10

The second parameter of the function assigned in webpack.config.js to module.exports contains the mode flag (--mode [development|production]). So here you can use the mode to load MiniCssExtractPlugin.loader or style-loader.

While developing, using style-loader is faster than extracting the styles each time. But in production, you should extract the styles in separate files to avoid the loading glitch in your web, when the styles load after the HTML, and you see your page without styles for a moment.

module.exports = (_, { mode }) => ({
  // other options here
  module: {
    rules: [
      // other rules here
      {
        test: /\.s?css$/i,
        use: [
          mode === 'production'
            ? MiniCssExtractPlugin.loader
            : 'style-loader',
          'css-loader',
          'sass-loader'
        ],
      },
    ],
  },
});
Quatrefoil answered 4/11, 2019 at 11:13 Comment(1)
Please, add some explanations about what the code in your answer does and why it should solve the issueBombshell
S
3

MiniCssExtractPlugin says actually you cannot do that:

This plugin should be used only on production builds without style-loader in the loaders chain, especially if you want to have HMR in development.

Snook answered 22/4, 2019 at 23:34 Comment(0)
E
3

if you like to use a specific loader for one dev environment and another one for production then i suggest that you webpack-merge , which will allow you to write two separate webpack configuration files based on the env.mode varriable here is an example :

this is my main webpack config file :

const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const webpackMerge = require("webpack-merge");
const path = require("path");

const modeConfig = env => require(`./build-utils/webpack.${env}`)(env);

module.exports = ({ mode } = { mode: "production" }) =>
  webpackMerge(
    {
      mode,
      entry: ['./src/index.js'],
      output: {
        filename: 'app.js',
        path: path.resolve(__dirname, "dist")
      },
      module: {
        rules: [
          {
            test: /\.(js|jsx)$/,
            exclude: /node_modules/,
            use: {
                loader: "babel-loader"
            }
        }
        ]
      },
      plugins: [
        new MiniCssExtractPlugin({
            filename: '[name].css',
            chunkFilename: '[id].css'
        })
      ]
    },
    modeConfig(mode)
  );

As you can see in line 6 i have declared a modeConfig variable that is a function that returns the value of require based on the env varriable passed through the cli command.

And now Create your webpack.production.js under build-utils folder this folder is going to contain just the config for the production evn :

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
   module.exports = () => ({
        module: {
        rules: [
            {
                test: /\.(sass|scss)$/,
                use: [
                    "style-loader",
                    "css-loader",
                    "sass-loader"
                ]
            }
        ]
    }
   });

Next your Dev config file

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = () => ({
            module: {
            rules: [
                {
                    test: /\.(sass|scss)$/,
                    use: [
                        {
                           loader: MiniCssExtractPlugin.loader,
                           options: {
                             hmr: true // since u know this is dev env
                           }
                        },
                        "css-loader",
                        "sass-loader"
                    ]
                }
            ]
        },
        devServer: {
          contentBase: path.join(__dirname, 'dist'),
          compress: true,
          hot: true,
          port: 3000
         }
       });

Now all you need is run the command

webpack --env.mode=production

or

webpack --env.mode=development
Estimable answered 8/9, 2019 at 0:57 Comment(1)
To minify the CSS 3 and the Javascript files in production I needed to import const TerserPlugin = require("terser-webpack-plugin"); and set it on optimization: { minimizer: [new OptimizeCssAssetsPlugin(), new TerserPlugin()], },, otherwise only the CSS 3 files were minifiedTollmann

© 2022 - 2024 — McMap. All rights reserved.