『Webpack系列』—— MiniCssExtractPlugin插件用法详解

12,682 阅读3分钟

一、插件的作用

This plugin extracts CSS into separate files. It creates a CSS file per JS file which contains CSS. It supports On-Demand-Loading of CSS and SourceMaps.

用某度翻译后:“该插件将CSS提取到单独的文件中。它为每个包含CSS的JS文件创建一个CSS文件。它支持CSS和SourceMap的按需加载。”

包含CSS的JS文件,JS文件不是指组件对应的JS文件,是指打包后生成的JS文件。

二、插件的优点

在Webpack4.0之前,是用ExtractTextWebpackPlugin插件来提取css的,与其相比有如下优点

  • 异步加载
  • 没有重复的编译(性能)
  • 更容易使用
  • 特定于CSS

三、插件的简单用法

搭建个简单的Wenpack Demo,演示一下。

先安装MiniCssExtractPlugin插件。

npm install --save-dev mini-css-extract-plugin

安装成功后,在webpack.config.js文件中配置。

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
    entry: {
        main: "./src/main.js",
    },
    plugins:[
        new MiniCssExtractPlugin(),
    ],
    module:{
        rules:[
            {
                test: /\.css$/,
                use:[MiniCssExtractPlugin.loader,'css-loader']
            },
        ]
    }
}

入口文件为main.js

import './css/main.css'
import './index'
console.log('main')

index.js

import './css/index.css'
import './index'
console.log('main')

main.cssindex.css内容一样

.class{
    color:red
}

执行命令webpack,可以在dist文件夹中看到打包生成的三个文件

项目中有两个包含CSS的JS文件,但是只打包生成了一个CSS文件,证实了上面 “它为每个包含CSS的JS文件创建一个CSS文件”中的包含CSS的JS文件是指打包后生成的JS文件

在浏览器打开index.html,在开发者工具中可以看到如下图所示

打包生成的CSS文件用link标签引入

如果没有用MiniCssExtractPlugin插件呢,改一下webpack.config.js文件中配置。

module:{
    rules:[
        {
            test: /\.css$/,
            use:['style-loader','css-loader']
        },
    ]
}

再执行webpack命令。在浏览器打开index.html,会发现样式在头部用<style></style>嵌入式引入。

看到这里也应该明白了MiniCssExtractPlugin插件的作用,就是提取JS中的CSS样式,用link外部引入,减少JS文件的大小,简称CCSS样式分离。

MiniCssExtractPlugin插件不能和style-loader共用

四、插件的参数

filename

控制从打包后的入口JS文件中提取CSS样式生成的CSS文件的名称。

module.exports = {
    entry: {
        main: "./src/main.js",
    },
    plugins:[
        new MiniCssExtractPlugin({
            filename: '[name]-test.css',
        }),
    ]
}

执行webpack命令,可以在dist文件夹中看到打包生成的CSS文件名称为main-test.css

chunkFilename

控制从打包后的非入口JS文件中提取CSS样式生成的CSS文件的名称。

module.exports = {
    entry: {
        main: "./src/main.js",
    },
    plugins:[
        new MiniCssExtractPlugin({
            filename: '[name]-test.css',
            chunkFilename: '[name]-test.css'
        }),
    ]
}

main.js

import './css/main.css'
import(/*webpackChunkName: "index"*/ './index')
console.log('main')

执行webpack命令,可以在dist文件夹中看到非入口JS文件index.bundle.js提取出来的CSS文件名为index-test.css

moduleFilename

该参数的值是个函数,主要应用多入口场景,控制从打包后的入口JS文件中提取CSS样式生成的CSS文件的名称。

如果和filename参数共用,filename将不起作用。

module.exports = {
    entry: {
        main: "./src/main.js",
        main1: "./src/main1.js",
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: 'css/[name].css',
            moduleFilename: (
                { name }) => {
                    return `${name}.css`
                },
        }),
    ],
}

执行webpack命令,打包生成的文件如下图所示

ignoreOrder

控制css的引入顺序不一致是否警告,true表示警告,false表示不警告。

简单的说,就是在js里css的引入顺序导致的问题,多个css的在js里的引入顺序不同,就会有这个警告。例如,在1.js 里,引入的顺序是a.css, b.css; 在2.js里,引入顺序是b.css,a.css,出现了这种引入顺序不同,就导致了警告。在两个js里把引入顺序调成一致,就没问题了。在1.js和2.js里的引入顺序都调整成a.css, b.css 就没有那个警告了。

当然也可以设置ignoreOrder参数为false,关闭这些警告。

具体可以看官网的这个issue

五、插件loader的参数

publicPath

默认值是webpackOptions.output,如下面所示,默认值就是path.resolve(__dirname, 'dist')

module.exports = {
    output: {
        filename: "[name].bundle.js",
        path: path.resolve(__dirname, 'dist'),
    },
}

作用是指定目标文件的定制公共路径。有点难理解,下面用一个实例来解释。

main.css

body{
    background: url('./LOGO.png');
}

main.js入口文件

import './css/main.css'
console.log('main')

因为main.css中引入图片,要用file-loader处理一下。在webpack.config.js中这么配置

module.exports = {
    module: {
        rules: [
            {
                test: /\.png$/,
                use: [
                    {
                        loader: 'file-loader',
                        options: {
                            name: 'img/[name].[hash:8].[ext]'
                        }
                    }
                ]
            },
        ],
    },
};

执行webpack命令,打包生成的CSS文件如下main.css如下所示 图片放在dist文件夹下img文件夹中,这时在浏览器打开index.html,图片是可以加载出来。

如果改一下生成的CSS文件路径。

module.exports = {
    plugins:[
        new MiniCssExtractPlugin({
            filename: 'css/[name].css',
        }),
    ]
}

再执行webpack命令,打包生成的CSS文件如下main.css如下所示 图片还是放在dist文件夹下img文件夹中,但是在浏览器打开index.html图片无法加载出来

因为main.css 中图片的引用路径是img/LOGO.af6d901d.png,显然路径不对,所以图片无法加载出来,要把图片的引用路径改成../img/LOGO.af6d901d.png才可以加载出来。

但是不可能去修改打包生成的文件,那要如何解决这个错误呢?此时publicPath参数的作用就体现出来了,可以利用publicPath参数来修改图片的引用路径,修改一下webpack.config.js的配置。

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    {
                        loader:MiniCssExtractPlugin.loader,
                        options:{
                            publicPath:'../'
                        }
                    },
                'css-loader']
            },
        ],
    }
}

再执行webpack命令,打包生成的CSS文件如下main.css如下所示

在浏览器打开index.html图片可以加载出来

publicPath的作用指定目标文件的定制公共路径,可以理解为指定../img/LOGO.af6d901d.pngimg前面的路径../

esModule

默认值为false,代表MiniCssExtractPlugin生成使用CommonJS模块语法的JS模块。

若为true,代表MiniCssExtractPlugin生成使用ES6模块语法的JS模块。

hmr

默认为false,为true时启动样式文件热更新。

在不启用样式文件热更新时,修改CSS源文件的时候,修改的样式不会自动在页面上显示,需要手动刷新页面,才会加载变化。启动样式文件热更新后,不再需要刷新整个页面,修改的样式就可以自动在页面上显示。

上面说到过MiniCssExtractPlugin插件不能和style-loader共用,一般在开发环境中配置style-loader,在生产环境中配置MiniCssExtractPlugin插件。其实style-loader也可以实现样式文件热更新。

HMR is an opt-in feature that only affects modules containing HMR code. One example would be patching styling through the style-loader. In order for patching to work, the style-loader implements the HMR interface; when it receives an update through HMR, it replaces the old styles with the new ones.

但是MiniCssExtractPlugin插件的样式文件热更新比style-loader更强大一些。比如说在public文件夹中的index.html引入一个CSS样式文件,如果修改了这个CSS样式文件的源码,使用style-loader是不会触发样式文件热更新,因为style-loader只热更新JS中引入的样式,但MiniCssExtractPlugin插件会热更新加载所有的样式。

一般只在生产环境中将hmr设置为true

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    {
                        loader:MiniCssExtractPlugin.loader,
                        options:{
                            publicPath:'../',
                            hmr: process.env.NODE_ENV === 'development',
                        }
                    },
                'css-loader']
            },
        ],
    }
}

reloadAll

if hmr does not work, this is a forceful method.

一般配合hrm参数使用,为true时,表示样式文件热更新不起作用时重新加载全部样式。

module.exports = {
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    {
                        loader:MiniCssExtractPlugin.loader,
                        options:{
                            publicPath:'../',
                            hmr: process.env.NODE_ENV === 'development',
                            reloadAll: true
                        }
                    },
                'css-loader']
            },
        ],
    }
}

六、将所有CSS提取到一个文件中

用SplitChunks插件去实现。SplitChunks插件的具体用法可以看我另一篇文章:Webpack之SplitChunks插件用法详解

module.exports = {
    optimization: {
        splitChunks: {
            cacheGroups: {
                styles: {
                    name: 'styles',
                    test: /\.css$/,
                    chunks: 'all',
                    enforce: true,
                },
            },
        },
    },
}

执行webpack命令,打包生成的文件如下如下所示