react-app-rewired 重写 CRA 配置

697 阅读2分钟

使用 react-app-rewired 和 customize-cra 来重写 CRA 的配置

react-app-rewired

react-app-rewired 文档

该工具可以在不 'eject' 也不创建额外 react-scripts 的情况下修改 create-react-app 内置的 webpack 配置,然后你将拥有 create-react-app 的一切特性,且可以根据你的需要去配置 webpack 的 plugins, loaders 等。

从 Create React App 2.0 开始,这个仓库主要由社区“轻度”维护。

使用了 react-app-rewired 之后,等于你得到了项目的配置权,但这表示你的项目将无法得到 CRA 提供的配置“保证”,希望你知道自己要做什么。

"Stuff can break" — Dan Abramov twitter.com/dan_abramov…

customize-cra

customize-cra API 文档

该项目提供了一套实用工具,用于利用 react-app-rewired 核心功能定制 create-react-app 2 和 3 版本的配置。

使用介绍

基本使用流程

  1. 安装依赖 yarn add react-app-rewired customize-cra -D
  2. 根目录下创建 config-overrides.js 文件夹
  3. 替换 package.json 中 scripts 执行部分
/* package.json */
  "scripts": {
-   "start": "react-scripts start",
+   "start": "react-app-rewired start",
-   "build": "react-scripts build",
+   "build": "react-app-rewired build",
-   "test": "react-scripts test",
+   "test": "react-app-rewired test",
    "eject": "react-scripts eject"
  }

config-overrides.js 配置

const {
  override,
  adjustStyleLoaders,
  addLessLoader,
  fixBabelImports,
  addBundleVisualizer,
  addWebpackAlias,
} = require("customize-cra");
const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent");
const path = require("path");

const rmPostcss = () => (config) => {
  const rules = config.module.rules.find((rule) =>
    Array.isArray(rule.oneOf)
  ).oneOf;
  rules.forEach(
    (r) =>
      r.use &&
      r.use.forEach((u) => {
        if (
          u.options &&
          u.options.postcssOptions &&
          u.options.postcssOptions.ident === "postcss"
        ) {
          delete u.options.postcssOptions.plugins;
          delete u.options.postcssOptions.config; // this makes the loader load the default config file
        }
      })
  );
  return config;
};

module.exports = {
  // 修改开发或生产编译 react 应用程序时使用的 Webpack 配置
  webpack: override(
    // 添加 post-css plugins
    // 注意📢: react-scripts 大于等于 5.0.0 版本时 addPostcssPlugins 无效
    // 解决方法:将 plugins 配置删除,使用如下 rmPostcss(),根目录添加 postcss.config.js 配置文件
    // addPostcssPlugins([
    //   require("postcss-pxtorem")({
    //     rootValue: 192,
    //     propList: ["*"],
    //     exclude: /node_modules|folder_name/i,
    //   }),
    // ]),
    rmPostcss(),

    // 添加less支持,需要安装 less 和 less-loader
    addLessLoader({
      lessOptions: {
        javascriptEnabled: true,
      },
    }),

    // 修改所有样式相关 loader 的配置
    adjustStyleLoaders(({ use: [, css, postcss, resolve, processor] }) => {
      // 为 .less 文件开启 css module
      if (processor && processor.loader.includes("less-loader")) {
        css.options.modules = {
          getLocalIdent: getCSSModuleLocalIdent,
          exportLocalsConvention: "camelCase",
        };
      }
      // 解决添加 addLessLoader 后报错[PostCSS Loader has been initialized using an options object that does not match the API schema. - options has an unknown property 'plugins'. These properties are valid]
      const postcssOptions = postcss.options;
      postcss.options = { postcssOptions };
    }),

    // 如果 antd 用的4.x 版本,组件和样式动态引入,使用时,需要安装 babel-plugin-import
    fixBabelImports("import", {
      libraryName: "antd",
      libraryDirectory: "es",
      style: "css",
    }),

    // 添加别名配置
    addWebpackAlias({
      "@": path.resolve(__dirname, "src"),
    }),

    // 生产环境清除 console.log
    function dropConsole(config) {
      const TerserPlugin = config.optimization.minimizer.find(
        (i) => i.constructor.name === "TerserPlugin"
      );
      if (TerserPlugin) {
        TerserPlugin.options.minimizer.options.compress["drop_console"] = true;
      }
      return config;
    },

    // 分析 bundle, 需要确保安装 webpack-bundle-analyzer
    addBundleVisualizer()
  ),
};

postcss.config.js

const fs = require("fs");
const path = require("path");

const useTailwind = fs.existsSync(
  path.resolve(__dirname, "tailwind.config.js")
);

module.exports = {
  plugins: !useTailwind
    ? [
        "postcss-flexbugs-fixes",
        [
          "postcss-preset-env",
          {
            autoprefixer: {
              flexbox: "no-2009",
            },
            stage: 3,
          },
        ],
        // Adds PostCSS Normalize as the reset css with default options,
        // so that it honors browserslist config in package.json
        // which in turn let's users customize the target behavior as per their needs.
        "postcss-normalize",
        require("postcss-pxtorem")({
          rootValue: 192, // 表示根元素字体大小
          propList: ["*"], // 可以从 px 变为 rem 的属性
          exclude: /node_modules|folder_name/i, // 需要排除的文件
        }),
      ]
    : [
        "tailwindcss",
        "postcss-flexbugs-fixes",
        [
          "postcss-preset-env",
          {
            autoprefixer: {
              flexbox: "no-2009",
            },
            stage: 3,
          },
        ],
      ],
};