Understanding and implementation of automatic recognition of css modules

foreword

css module The advantage is that conflicts can be avoided  namespace , especially when the project is relatively large. import cls from 'XXX.css'Today, when I wanted to use the import style in the react project I built myself css modules, I found the following situation on the page↓, and I found out after printing it.cls = underfined

image.png image.png

Reason: css-loaderThe optons.modulesdefault configuration is undefinedthat it needs to match a specific regular to enablecss modules

image.png

The key issue

  • Question : Normal activation moduleswill make the styles that you want to use as global lose their effect, because they are all converted into unique values. If you do not open them globally, you can only specify the suffix name to distinguish them.css modules
  • The right way: let webpackautomatic recognition be enabledcss modules

ideas

Final goal: open modules in css-loader according to rule matching

  1. To distinguish whether it is enabled or not css modulesis to distinguish import './index.css'and to see what is the difference between import cls from './index.css'the corresponding conversion to AST
  2. css modulesLabel the enabled files according to this different field
  3. Use webpackthe rulesrule matching field oneOf to match

image.png

Refer to   the implementation of umi css module

Source code: babel-plugin-auto-css-modules By writing a Babelplug- in ,import adding parameters to the , matching this parameter, and performing different configurations.urlwebpack

babel plugin

// babel插件 --- 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';
        }
      },
    },
  };
};

复制代码

.babelrcIntroduce plugins in

{
  "presets": [
    "@babel/env",
    [
      "@babel/preset-react",
      {"runtime": "automatic"}
    ],
  ],
  "plugins": ["./css-modules"]
}
复制代码

Test whether the plugin is effectively imported

if (
  specifiers.length > 0 &&
  CSS_FILE_EXTENSIONS.includes(extname(value))
) {
  console.log(value); // 启动webpack 有看到打印值就可以了
  source.value = `${value}?css_modules`;
}
复制代码

configured webpack_rules

const rules = [
  {
    test: /\.(js|jsx|ts|tsx)$/,
    exclude: /(node_modules|bower_components)/,
    use: 'babel-loader',
  },
  {
    test: /\.css$/,
    oneOf: [
      {
        resourceQuery: /css_modules/,
        use: [
          { loader: 'style-loader' },
          { loader: 'css-loader', options: { modules: true } },
          // { loader: 'less-loader' }, less加上即可
        ],
      },
      {
        use: [
          { loader: 'style-loader' },
          { loader: 'css-loader' },
          //{ loader: 'less-loader' },
        ],
      },
    ],
  },
];
复制代码

Use  the ability of resourceQuery  to query whether there are specific parameters to determine whether to enable  css-module the function. Achieving the form without modifying the file name, compatible with common  css and  css-module style files, simple and elegant :)

final effect

image.png

image.png

CSS modules can be used normally without special suffix names, and global styles are also inserted into the DOM normally, prefect.

Reference documentation

Guess you like

Origin juejin.im/post/7080058017906950152