❌ Create React App / craco.config.js 配置(已淘汰)

21 阅读3分钟

Create React App

说明:CRA 已被 React 官方弃用,新项目优先 Vite 等。本文档保留安装说明及 在不 eject 前提下用 CRACO 改配置 的实操(多来自旧项目栈)。

Create React App 是官方曾推荐的单页 React 脚手架,提供零配置的现代构建设置。

安装与卸载

安装

# npm
npm install -g create-react-app@<version>

# yarn
yarn global add create-react-app@<version>

卸载

npm uninstall -g create-react-app
yarn global remove create-react-app

查看版本

create-react-app --version
npm view create-react-app versions --json

创建项目

npx create-react-app@latest <project-name>
npx create-react-app <project-name> --template typescript

npm init react-app <project-name>
npm init react-app <project-name> --template typescript

yarn create react-app <project-name>
yarn create react-app <project-name> --template typescript

目录结构

文件/目录说明
public/静态文件
public/robots.txt搜索引擎爬取规则
public/manifest.jsonPWA 配置
src/react-app-env.d.tsTS 声明
src/reportWebVitals.ts性能埋点
src/setupTests.ts单元测试配置

自定义配置:eject 与 CRACO

官方文档 将 webpack 等配置封装在 react-scripts 内。要改配置有两种常见方式:

方式说明
npm run eject弹出全部配置,不可逆,后续自行维护 webpack
CRACO不 eject,用 craco.config.js 覆盖/扩展配置(推荐旧 CRA 项目)

npm init react-app 会安装 react-scripts(已集成 webpack、babel、postcss 等),因此 CRACO 侧通常只需补装插件包。

安装 CRACO

npm i @craco/craco -D
# Less 支持
npm i craco-less -D

根目录新建 craco.config.js,并改 package.json

{
  "scripts": {
    "start": "craco start",
    "build": "craco build",
    "eject": "react-scripts eject"
  }
}

多环境变量(dotenv-cli)

npm i dotenv-cli -D
{
  "scripts": {
    "dev": "craco start",
    "prod": "dotenv -e .env.production craco start",
    "build:dev": "dotenv -e .env.development craco build",
    "build:test": "dotenv -e .env.test craco build",
    "build:prod": "dotenv -e .env.production craco build"
  }
}

Less(含 antd 主题变量)

react-scripts 已带 lessless-loader 依赖时,安装 craco-less 即可:

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

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: {
              "@primary-color": "rgb(0, 82, 204)",
              "@font-size-base": "16px",
            },
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

Webpack 别名

const path = require("path");

module.exports = {
  webpack: {
    alias: {
      "@": path.resolve(__dirname, "src"),
    },
  },
};

devServer(端口、代理)

module.exports = {
  devServer: {
    port: 8888,
    hot: true,
    client: { overlay: false },
    proxy: {
      "/api": {
        target: process.env.REACT_APP_URL,
        changeOrigin: true,
      },
    },
  },
};

生产环境:拆包、publicPath、去 source map

const isProd = process.env.NODE_ENV === "production";

module.exports = {
  webpack: {
    configure: (webpackConfig) => {
      if (isProd) {
        webpackConfig.devtool = false;
        webpackConfig.optimization = {
          splitChunks: {
            chunks: "async",
            minSize: 40000,
            cacheGroups: {
              antd: {
                name: "chunk-antd",
                chunks: "all",
                test: /[\\/]node_modules[\\/](@ant-design|antd-mobile)[\\/]/,
                priority: -7,
              },
              common: {
                name: "chunk-common",
                chunks: "all",
                test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom)[\\/]/,
                priority: -9,
              },
            },
          },
        };
        webpackConfig.output = {
          ...webpackConfig.output,
          publicPath: "./",
        };
      }
      return webpackConfig;
    },
  },
};

拆包 cacheGroups 按项目依赖自行调整。分析体积可装 webpack-bundle-analyzer 并在 webpack.plugins 中启用。

生产环境移除 console

const isProd = process.env.NODE_ENV === "production";

module.exports = {
  babel: {
    plugins: [
      [
        "babel-plugin-transform-remove-console",
        { exclude: isProd ? ["error", "warn"] : ["error", "warn", "log"] },
      ],
    ],
  },
};

移动端 rem(归档参考,不推荐新项目)

lib-flexible 已废弃;现代方案见 vw/postcss-px-to-viewport 等。仅维护旧 CRA 项目时参考。

npm i lib-flexible postcss-pxtorem -D

入口 import 'lib-flexible'。PostCSS 片段:

const postcssPx2Rem = require("postcss-pxtorem");

module.exports = {
  style: {
    postcss: {
      mode: "extends",
      loaderOptions: {
        postcssOptions: {
          plugins: [
            postcssPx2Rem({
              rootValue: 37.5,
              propList: ["*"],
              exclude: /node_modules/i,
            }),
          ],
        },
      },
    },
  },
};

craco.config.js 合并示例

以下为旧项目常用合并写法,拆包分组请按实际依赖修改。

const path = require("path");
require("react-scripts/config/env");

const CracoLessPlugin = require("craco-less");
const isProd = process.env.NODE_ENV === "production";
const postcssPx2Rem = require("postcss-pxtorem");
const url = process.env.REACT_APP_URL;

module.exports = {
  webpack: {
    alias: { "@": path.resolve(__dirname, "src") },
    configure: (webpackConfig) => {
      if (isProd) {
        webpackConfig.devtool = false;
        webpackConfig.optimization = {
          splitChunks: {
            chunks: "async",
            minSize: 40000,
            maxAsyncRequests: 10,
            maxInitialRequests: 10,
            automaticNameDelimiter: "~",
            name: false,
            cacheGroups: {
              antd: {
                name: "chunk-antd",
                chunks: "all",
                test: /[\\/]node_modules[\\/](@ant-design|antd-mobile)[\\/]/,
                priority: -7,
              },
              common: {
                name: "chunk-common",
                chunks: "all",
                test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-redux|react-router-dom)[\\/]/,
                priority: -9,
              },
              vendor: {
                name: "chunk-vendor",
                chunks: "all",
                test: /[\\/]node_modules[\\/](axios|lodash|core-js)[\\/]/,
                priority: -10,
              },
            },
          },
        };
        webpackConfig.output = { ...webpackConfig.output, publicPath: "./" };
      }
      return webpackConfig;
    },
  },
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: { javascriptEnabled: true },
        },
      },
    },
  ],
  style: {
    postcss: {
      mode: "extends",
      loaderOptions: () => ({
        postcssOptions: {
          ident: "postcss",
          config: false,
          plugins: [
            postcssPx2Rem({
              rootValue: 37.5,
              propList: ["*"],
              exclude: /node_modules/i,
            }),
          ],
        },
        sourceMap: false,
      }),
    },
  },
  babel: {
    plugins: [
      [
        "babel-plugin-transform-remove-console",
        { exclude: isProd ? ["error", "warn"] : ["error", "warn", "log"] },
      ],
    ],
  },
  devServer: {
    port: 8888,
    hot: true,
    client: { overlay: false },
    proxy: {
      "/": {
        target: url,
        changeOrigin: true,
        pathRewrite: { "^/": "" },
      },
    },
  },
};

Prettier

统一代码风格。

yarn add prettier eslint-config-prettier -D

prettierrc.json 示例:

{
  "singleQuote": true,
  "semi": false,
  "trailingComma": "none"
}

.prettierignorebuildcoverage

package.json 中与 ESLint 共存:

{
  "eslintConfig": {
    "extends": ["react-app", "react-app/jest", "prettier"]
  }
}

VS Code:editor.formatOnSave + esbenp.prettier-vscode


Husky + lint-staged

yarn add husky@8.0.3 lint-staged@15.2.10 -D
{
  "scripts": { "prepare": "husky install" },
  "lint-staged": {
    "*.{js,css,md,ts,tsx}": "prettier --write"
  }
}

.husky/pre-commitnpx lint-staged。执行 yarn prepare 初始化。


Commitlint

yarn add @commitlint/cli @commitlint/config-conventional -D

commitlint.config.jsmodule.exports = { extends: ['@commitlint/config-conventional'] };

.husky/commit-msgnpx --no-install commitlint --edit $1