关于自动识别css modules的理解与实现

前言

css module 的好处是可以避免 namespace 冲突的问题,尤其是项目工程比较庞大的情况。今天在自己搭建的react项目中想用import cls from 'XXX.css'导入css modules样式的时候,发现页面出现以下情况 ↓ , 打印了一下才发现cls = underfined

image.png image.png

原因: css-loaderoptons.modules配置默认是undefined,需要匹配特定的正则才能启用css modules

image.png

关键问题

  • Question: 正常的启用modules会让想作为全局的样式失去效果,因为都被转成唯一值了, 不全局开启只能规定后缀名去区分使用css modules
  • 正确做法:让webpack自动识别是否启用css modules

思路

最终目标:根据规则匹配在css-loader中开启modules

  1. 区分是否启用css modules,其实就是区分import './index.css'import cls from './index.css',看看对应转成AST哪里有区别
  2. 根据这个不同的字段去给启用css modules的文件打标签
  3. webpackrules规则匹配字段oneOf进行匹配

image.png

参考 umi css module 的实现

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

babel插件

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

复制代码

.babelrc中引入插件

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

测试插件是否有效引入

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

配置webpackrules

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' },
        ],
      },
    ],
  },
];
复制代码

利用 resourceQuery 的能力,查询是否存在特定的参数,来决定是否启用 css-module 的功能。达到了不用修改文件名的形式,兼容了普通 css 和 css-module 的样式文件,简单而优雅:)

最终效果

image.png

image.png

不用特殊后缀名也可以正常使用css modules,全局样式也正常插入DOM中,prefect。

参考文档

猜你喜欢

转载自juejin.im/post/7080058017906950152