以前天真地以为react-css-modules使用与其他插件一样,npm install安装一下就好了。

直到我新起了一个项目才知道并没有这么简单,react-css-modules使用是需要修改Webpack配置的。

如果不配置webpack,会报:Uncaught Error: "xxx" CSS module is undefined.等错误

关于react-css-modules怎么用,可参照这篇:【React】防止CSS样式感染:react-css-modules

Webpack配置

配置Webpack的目的是开启css-loader模块化(modules)。

Webpack配置如下:

module.exports = {
  module: {
    //模块
    rules: [
      {
        //.css.less文件解析
        test: /\.(css|less)$/, //匹配到css结尾的文件,加载css-loader,
        //去除.module.css;.module.less,因为有单独处理
        exclude: [/\.module\.(css|less)/, /\.global\.less$/],
        use: [
          {
            loader: 'style-loader',
            options: {
              //将当前loader添加到<head>标签内容的最上面
              insert: function (element) {
                var parent = document.querySelector('head')
                var lastInsertedElement =
                  window._lastElementInsertedByStyleLoader
                if (!lastInsertedElement) {
                  parent.insertBefore(element, parent.firstChild)
                } else if (lastInsertedElement.nextSibling) {
                  parent.insertBefore(element, lastInsertedElement.nextSibling)
                } else {
                  parent.appendChild(element)
                }
              }
            }
          },
          {
            //css单独分离文件加载
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: false
            }
          },
          'css-loader',
          'postcss-loader',
          'less-loader'
        ]
      },
      {
        //.module.css;.module.less文件解析,添加css modules,防止样式感染
        test: /\.module\.(css|less)/, //匹配到less结尾的文件
        use: [
          {
            loader: 'style-loader',
            options: {
              //将当前loader添加到<head>标签内容的最上面
              insert: function (element) {
                var parent = document.querySelector('head')
                var lastInsertedElement =
                  window._lastElementInsertedByStyleLoader
                if (!lastInsertedElement) {
                  parent.insertBefore(element, parent.firstChild)
                } else if (lastInsertedElement.nextSibling) {
                  parent.insertBefore(element, lastInsertedElement.nextSibling)
                } else {
                  parent.appendChild(element)
                }
              }
            }
          },
          {
            //css单独分离文件加载
            loader: MiniCssExtractPlugin.loader,
            options: {
              esModule: false
            }
          },
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[local]_[hash:base64:5]'
              }
            }
          },
          'postcss-loader',
          'less-loader'
        ]
      }
    ]
  }
}

核心原理

  1. .css.less文件样式用普通css-loader 加载;
  2. .module.css.module.less文件样式开启modules模块化,并配置样式class名字的生成规则;

Webpack5 modules 配置如下:

          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[local]_[hash:base64:5]'
              }
            }
          },

Webpack4 modules 配置如下:

                    {
                        loader: 'css-loader',
                        options: {
                            sourceMap: false,
                            importLoaders: 1,
                            modules: true,
                            localIdentName: '[local]_[hash:base64:5]'
                        }
                    },

localIdentName 是配置class名字的规则,[local]_[hash:base64:5]表示:用代码里的class名字+下划线+5位hash值

因为hash值是随机且唯一的,所以拼接之后的新名字也是唯一的,这就是css-modules防止样式感染的原理。

注意:.css.less文件与.module.css.module.less文件分开配置,是为了防止.css.less文件里的样式也被混淆,否则如果代码里用到了className引用样式,就会找不到。

所以需要使用css-modules的功能,需要将样式文件命名成文件名.module.css等格式,作为和纯css文件的区分。

好了,又记完一坑,希望你能成功!