欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

create-react-app 之 使用 @craco/craco 和 craco-less 实现支持 css-modules 的解决方案

程序员文章站 2024-03-01 09:10:52
...

使用 CRA 脚手架创建的项目,如果想要修改编译配置,通常可能会选择 npm run eject弹出配置后魔改。但是,eject 是不可逆操作,弹出配置后,你将无法跟随官方的脚步去升级项目的 react-script 版本。
如果想要无 eject 重写 CRA 配置,一般可以有以下几种方式:

  1. 通过 CRA 官方支持的 --scripts-version 参数,创建项目时使用自己重写过的 react-scripts
  2. 使用 react-app-rewired + customize-cra 组合覆盖配置
  3. 使用 craco 覆盖配置

在 AntDesign 官方文档中,已经推荐使用 craco 来覆写相关 webpack 配置;

# 安装 @craco/craco
yarn add @craco/craco

由于 CRA 脚手架默认不支持 less,所以需要通过拓展来实现:

# 安装 craco-less 包,支持覆写 webpack loader 相关配置
yarn add craco-less -D

# 同时需要安装 less 和 less-loader
yarn add less less-loader -D

项目根目录下新建一个 craco.config.js 文件:

const CracoLessPlugin = require("craco-less");

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: {},
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

问题来了:如果要支持 css-modules ,该如何处理?

在实践时,按照官方提供的文档,没法实现支持 css-modules 的功能,官方文档提供的配置方法如下:

const CracoLessPlugin = require("craco-less");

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        cssLoaderOptions: {
          modules: { localIdentName: "[local]_[hash:base64:5]" },
        },
      },
    },
  ],
};

如果需要解决这个问题,这里有两个方案:

方案一:继续使用 craco-less 来进行配置,但是需要新增一个配置选项,配置代码如下所示:【推荐】

// craco.config.js
const CracoLessPlugin = require("craco-less");

const lessModuleRegex = /\.module\.less$/;

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: { "@primary-color": "#1DA57A" },
            javascriptEnabled: true,
          },
        },
        // A callback function that receives two arguments: the webpack rule, and the context. You must return an updated rule object.
        modifyLessRule: (lessRule, context) => {
            lessRule.test = lessModuleRegex;
            lessRule.exclude = /node_modules|antd\.css/;
            return lessRule;
        },
        // Passing an options object to configure the css-loaders
        cssLoaderOptions: {
            modules: { localIdentName: "[local]_[hash:base64:5]" },
        },
      },
    },
  ],
};

方案二:不再使用 craco-less,改用 craco-antd;如果在项目中使用了 AntDesign 的UI库,那么 craco-antd 能让你更容易去使用它们。

This is a craco plugin that makes it easy to use the Ant Design UI library with create-react-app version >= 2.

实际上,craco-antd 包括了对 less 的支持,是需要依赖 craco-less 的;如果安装 craco-antd 则不需要单独安装 craco-less

如果依赖的版本如下所示:

“craco-antd”: “^1.18.0”,
“@craco/craco”: “^5.6.4”,
“craco-less”: “^1.17.0”,

可以参考如下配置:

const CracoAntDesignPlugin = require("craco-antd");

const lessModuleRegex = /\.module\.less$/;

module.exports = {
  plugins: [
    {
      plugin: CracoAntDesignPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            javascriptEnabled: true,
            sourceMap: false,
          },
        },
        // 支持 css-modules 
        cssLoaderOptions: {
          modules: {
            getLocalIdent: (context, localIdentName, localName, options) => {
              if (context.resourcePath.includes("node_modules")) {
                return localName;
              }
              return getCSSModuleLocalIdent(
                context,
                localIdentName,
                localName,
                options
              );
            },
          },
        },
        // 这个是按需导入
        babelPluginImportOptions: {
          libraryName: "antd",
          libraryDirectory: "es",
          style: true,
        },
      },
    },
  ],
};

如果直接安装最新版本的包,可以参考下面的代码:

const CracoAntDesignPlugin = require("craco-antd");
const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent");
const lessModuleRegex = /\.module\.less$/;

module.exports = {
  plugins: [
    {
      plugin: CracoAntDesignPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            javascriptEnabled: true,
            sourceMap: false,
          },
        },
        cssLoaderOptions: {
          modules: {
            // 通过这个生成的 classname 结构如:ComponentName_className_hash:5   
            getLocalIdent: (context, localIdentName, localName, options) => {
              if (context.resourcePath.includes("node_modules")) {
                return localName;
              }
              return getCSSModuleLocalIdent(
                context,
                localIdentName,
                localName,
                options
              );
            },
          },
        },
        modifyLessRule: (lessRule, context) => {
            lessRule.test = lessModuleRegex;
            lessRule.exclude = /node_modules|antd\.css/;
            return lessRule;
        },
        babelPluginImportOptions: {
          libraryName: "antd",
          libraryDirectory: "es",
          style: true,
        },
      },
    },
  ],
};

实际上,这个解决方案与方案一是一样的,如果 @craco/craco > 6.0.0 ,按照作者文档提供的配置方式,还是无法实现支持 css-modules,初步的怀疑就是最新版本的 craco 存在兼容问题及一些BUG,由于作者精力不够,维护的不是十分频繁,可能在某一个版本会修复该问题;

当然,也还可以同时使用 craco-lesscraco-antd ,两者配合使用也是可以的(建议调整 @craco/craco 版本低于 6.0.0),参考代码如下:

const CracoAntDesignPlugin = require("craco-antd");
const CracoLessPlugin = require('craco-less');

module.exports={
    plugins: [
        {
            plugin: CracoAntDesignPlugin,
        },
        {
            plugin: CracoLessPlugin,
            options: {
                modifyLessRule: function(lessRule, _context) {
                    lessRule.test = /\.(module)\.(less)$/;
                    lessRule.exclude = /node_modules/;
                    return lessRule;
                },
                cssLoaderOptions: {
                    modules: {
                        localIdentName: '[name]__[local]--[hash:base64:5]',
                    }
                },
                lessLoaderOptions: {
                    lessOptions: {
                        javascriptEnabled: true,
                    },
                }
            },
        },
    ]
}