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.json | PWA 配置 |
src/react-app-env.d.ts | TS 声明 |
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 已带 less、less-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"
}
.prettierignore:build、coverage。
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-commit:npx lint-staged。执行 yarn prepare 初始化。
Commitlint
yarn add @commitlint/cli @commitlint/config-conventional -D
commitlint.config.js:module.exports = { extends: ['@commitlint/config-conventional'] };
.husky/commit-msg:npx --no-install commitlint --edit $1