webpack 自动识别 css modules,cssModule混用

期望达到的效果

import Test from './test.less';  // 这样的引入需要css modules
import './test.less';            // 这样的不需要css modules

虽然 css-loader 自带一些配置可以用来配置是否开启当前文件cssModule(配置特殊的文件名,路径等),但是还是觉得没有上面的那种用起来方便。

参考 umi css module 的实现,源码:babel-plugin-auto-css-modules,通过写 Babel 插件,在 import 的 url 上加上参数,webpack 匹配这个参数,进行不同的配置。

这样的引入方式也可以消除开启 css module 后,ant design 组件样式出错的问题

Babel 插件

// scripts/plugin/auto-css-modules.js
const { extname } = require('path');
const CSS_EXTNAMES = ['.css', '.scss', '.sass', '.less'];
module.exports = () => {
  return {
    visitor: {
      ImportDeclaration(path) {
        const { specifiers, source } = path.node;
        const { value } = source;
        if (specifiers.length > 0 && CSS_EXTNAMES.includes(extname(value))) {
          source.value = `${value}?css_modules`; // 在路径末尾加上 css_modules 用于 webpack 匹配该文件,如 import Test from './test.less'; 变成 import Test from './test.less?css_modules';
        }
      },
    },
  };
};

.babelrc 中引入插件

{
  "presets": [
    "@babel/preset-react",
    "@babel/preset-env",
    "@babel/preset-typescript"
  ],
  "plugins": [
    "@babel/plugin-transform-runtime",
    "@babel/plugin-proposal-class-properties",
    "lodash",
    "./scripts/plugin/auto-css-modules.js", // 引入插件
    ["import", {
      "libraryName": "antd",
      "libraryDirectory": "es",
      "style": "css"
    }]
  ]
}

在 webpack 中进行配置

声明两个不同的loader配置

// 未开启 css module 的 loader
const cssLoader = [
  'style-loader',
  !isEnvDevelopment && {
    loader: MiniCssExtractPlugin.loader,
  },
  'css-loader',
  {
    loader: 'postcss-loader',
    options: {
      ident: 'postcss',
      plugins: () => [
        require('postcss-preset-env')({
          autoprefixer: {
            flexbox: 'no-2009',
          },
          stage: 3,
        }),
      ],
    },
  },
  {
    loader: 'less-loader',
    options: {
      javascriptEnabled: true,
    },
  },
];
// 开启 css module 的 loader
const cssModulesLoader = JSON.parse(JSON.stringify(cssLoader));
cssModulesLoader[2] = {
  loader: 'css-loader',
  options: {
    modules: {
      localIdentName: '[local]_[hash:base64:5]',
    },
  },
};

webpack rules 配置:

module: {
  rules: [
    {
      test: /\.(css|less)$/,
      oneOf: [
        {
          resourceQuery: /css_modules/, // 只要匹配到了这个,就是用css modules,
          use: cssModulesLoader.filter(Boolean),
        },
        {
          use: cssLoader.filter(Boolean),
        },
      ],
    },
  ]
}

这样配置后,就可以在项目中查看具体的效果了。。。。。