Create React App 是一个官方支持的创建 React 单页应用程序的方法。它提供了一个零配置的现代构建设置。
而在实际的项目开发中,我们通常希望可以自行配置一些参数。如:webpack 的alias
、typescript 的path
、eslint 的rules
等。本文记录了基于 create-react-app 打造 react+ts 的过程遇到的各种问题,以及对应的解决方法。
react-scripts: 4.0.3 (发布新版本的 Create React App,更新 react-scripts 即可 )
创建项目
创建 react + ts 项目: npx create-react-app my-app --typescript
安装 TypeScript 包: npm install --save typescript @types/node @types/react @types/react-dom @types/jest
将 .js、.jsx
文件重命名为 .ts、.tsx
文件(typeScript文件),运行 npm start
启动项目。
注:不需要手动创建 tsconfig.json
文件。当项目中的 src/
路径下有TypeScript文件(.ts|tsx
)时,运行 npm start
会自动创建tsconfig.json
文件。
自定义webpack
CRA 项目中,webpack 的配置是封装在 react-scripts
包内。扩展 webpack 配置有以下方法:
1. npm run eject
(不推荐)
它会将所有配置文件和传递依赖项(Webpack,Babel,ESLint等)复制到项目中,以便你可以完全控制它们。除 eject
之外的所有命令仍然有效,但它们将指向复制的脚本,以便你可以调整它们。
注意:这是单向操作。一旦你 eject
,你就不能回去了!
CRA 通过升级其中的 react-scripts
包来升级 CRA 的特性,而使用了 eject
命令,就再也享受不到 CRA 升级带来的好处了。因为 package
中的 scripts
的命令已经指向了项目中的配置文件,而不是指向 react-scripts
包。
2. react-app-rewired
+ customize-cra
安装:npm i react-app-rewired customize-cra -S
**配置:**在项目根目录添加 config-overrides.js
配置文件
该工具可以在不 npm run eject
, 也不创建额外 react-scripts
的情况下修改 create-react-app
内置的 webpack
配置,然后你将拥有 create-react-app
的一切特性,且可以根据你的需要去配置 webpack
的 plugins, loaders
等。
/* config-overrides.js */
module.exports = function override(config, env) {
//do stuff with the webpack config...
return config;
}
默认情况下, config-overrides.js
文件导出单个函数,以便在开发或生产模式下自定义 webpack 配置。此外,该文件中也可以导出一个包含最多三个字段的对象,每个字段都是一个函数。如下:
/* config-overrides.js */
module.exports = {
// 该字段与 config-overrides.js 导出的单个函数的等效项。
// 它无法在测试模式下配置编译,也不能用于自定义开发模式下的 Webpack Dev Server。
webpack: function(config, env) {
return config;
},
// 该配置应用于 Jest 模式下。这意味着上述 webpack 配置在测试模式下都是无效的。
jest: function(config) {
return config;
},
// 在开发模式下运行时,用于生成 dev server 配置。
devServer: function(configFunction) {
return function(proxy, allowedHost) {
const config = configFunction(proxy, allowedHost);
return config;
};
},
// paths 字段用于为 create-react-app 传递到 webpack 和 jest 的路径提供覆盖。
paths: function(paths, env) {
return paths;
}
}
使用 customize-cra。
customize-cra
用于改成 react-app-rewired
的 config-overrides.js
文件。通过导入 customize-cra
功能和导出几个函数调用包裹在 override
函数中。这样,就可以很容易地修改组成create-react-app
的配置对象(webpack
,webpack-dev-server
,babel
等)。
/* config-overrides.js */
const {
override,
disableEsLint,
addWebpackAlias,
} = require("customize-cra");
const path = require("path");
module.exports = override(
disableEsLint(),
addWebpackAlias({
["ag-grid-react$"]: path.resolve(__dirname, "src/shared/agGridWrapper.js")
})
);
* 修改项目启动命令。
```json
/* package.json */
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
}
自定义 config-overrides.js
路径。
比如:您想要使用在 node_modules
中的第三方 config-overrides.js
,您可以将以下内容添加到您的 package.json
:
"config-overrides-path": "node_modules/some-preconfigured-rewire"
craco
是另一款类似 react-app-rewired
的功能。
/* craco.config.js */
const { when, whenDev, whenProd, whenTest, ESLINT_MODES, POSTCSS_MODES } = require("@craco/craco");
module.exports = {
reactScriptsVersion: "react-scripts" /* (default value) */,
style: {},
eslint: {},
babel: {},
typescript: {},
webpack: {
alias: {
'@': path.resolve(__dirname, "src")
},
plugins: {
add: [],
remove: [],
},
configure: { },
// configure: (webpackConfig, { env, paths }) => { return webpackConfig; }
},
jest: { },
devServer: { },
plugins: [ ]
};
修改项目启动命令
/* package.json */
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
}
自定义Typescript
按上述安装方法创建项目后,运行 npm start
,在 src/
有 typescript 文件的情况下,会自动创建 tsconfig.json
文件。 你可以编辑该文件。
在建项过程中发现, 修改 tsconfig.json
文件中的 compilerOptions.paths
属性后,每次运行 npm start
,该属性会被自动创建的文件覆盖。
解决方案如下:
安装: npm i @craco/craco -D
新建 tsconfig.paths.json
文件
// tsconfig.paths.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
修改 tsconfig.json
文件。
// tsconfig.json
{
"extends": "./tsconfig.paths.json",
...
}
环境变量
CRA 创建的项目可以使用环境中声明的变量,就像是在 JS 文件中本地声明的变量一样。默认情况下,项目将为你定义 NODE_ENV
,以及以 REACT_APP_
开头的任何其他环境变量。
环境变量在构建期间嵌入。由于 Create React App 生成静态的 HTML / CSS / JS 包,因此无法在 runtime(运行时) 读取它们。项目中的环境变量,会在构建静态文件时,替换成对应的值。
console.log(process.env)
// FAST_REFRESH: true
// NODE_ENV: "development"
// PUBLIC_URL: ""
// WDS_SOCKET_HOST: undefined
// WDS_SOCKET_PATH: undefined
// WDS_SOCKET_PORT: undefined
NODE_ENV
是一个特殊内置环境变量。当你运行 npm start
时,它总是等于 'development'
,当你运行 npm test
它总是等于 'test'
,当你运行 npm run build
来生成一个生产 bundle(包) 时,它总是等于 'production'
。你无法手动覆盖NODE_ENV
。
注意:必须以 REACT_APP_
开头创建自定义环境变量。除了 NODE_ENV
之外的任何其他变量都将被忽略。更改任何环境变量都需要重新启动正在运行的开发服务器。
自定义环境变量
有两种方式完成自定义环境变量:在 shell 中或在 .env
文件中。
在 Shell 中添加临时环境变量
定义环境变量可能因操作系统而异。 注意,这种方式对于 shell 会话是暂时的。
# Windows (cmd.exe)
# 注意:变量赋值需要用引号包裹,以避免尾随空格。)
set "REACT_APP_SECRET_CODE=abcdef" && npm start
# Windows (Powershell)
($env:REACT_APP_SECRET_CODE = "abcdef") -and (npm start)
# Linux, macOS (Bash)
REACT_APP_SECRET_CODE=abcdef npm start
在 .env
中添加开发环境变量
要定义永久环境变量,请在项目的根目录中创建名为 .env
的文件:
# .env
REACT_APP_WEBSITE_NAME=REACT+TS项目
REACT_APP_AUTHOR=李兆
REACT_APP_SECRET_NUMBER=123456
环境变量间的引用:
REACT_APP_VERSION=$REACT_APP_SECRET_NUMBER
如何使用环境变量
在 HTML 中引用环境变量
<!-- index.htm l-->
<title>%REACT_APP_WEBSITE_NAME%</title>
在 JS 中引用环境变量
// index.js
console.log(process.env.REACT_APP_AUTHOR)
在 CSS 中引用环境变量
# .env.local
REACT_APP_MAIN_COLOR=red
方法一:使用行内样式
<!--行内样式-->
<div style=\{\{color: process.env.REACT_APP_MAIN_COLOR\}\}></div>
**方法二:**使用 CSS in JS
,即用 js 写 css,可用第三方库实现。如:Style Components
、Emotion
。
安装:npm i styled-components -D
// index.js
import styled from 'styled-components'
const WrapperDiv = styled.div`
color: ${process.env.REACT_APP_MAIN_COLOR}
`;
function App() {
return <WrapperDiv>css in js</WrapperDiv>;
}
export default App;
方法三:使用 CSS 预处理器来管理全局变量。如:sass。
安装:npm install node-sass -S
// craco.config.js
module.exports = {
style: {
sass: {
loaderOptions: {
additionalData: "$my-color: cyan;"
}
}
}
}
/* index.scss */
.env_variable {
color: $my-color;
}
其他 .env
文件
.env.local
:本地覆盖。除 test 之外的所有环境都加载此文件。
.env.development
, .env.test
, .env.production
:设置特定环境。
.env.development.local
, .env.test.local
, .env.production.local
:设置特定环境的本地覆盖。
左侧的文件比右侧的文件具有更高的优先级:
npm start
: .env.development.local
, .env.development
, .env.local
, .env
npm run build
: .env.production.local
, .env.production
, .env.local
, .env
npm test
: .env.test.local
, .env.test
, .env
。注意:没有 .env.local
代码规范化(Eslint\Prettier)
eslint配置
修改 eslant
配置,可直接修改 package.json
配置,或者添加 eslint.js
文件。
// package.json
{
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
],
"rules": {
'no-console': process.env.NODE_ENV === 'production' ? 2 : 1,
}
},
}
// eslintrc.js
module.exports = {
extends: [
"react-app",
"react-app/jest"
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 2 : 1,
},
}
方法二:修改 craco.config.js
配置。
module.exports = {
eslint: {
enable: true,
mode: "extends",
configure: {
extends: [
"react-app",
"react-app/jest"
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 2 : 1,
},
},
pluginOptions: {
},
},
}
prettier配置
安装:npm i prettier eslint-plugin-prettier eslint-config-prettier -D
**配置:**在 package.json
里添加 prettier
字段,或者在项目的根目录下添加 .prettierrc
或 .prettierrc.js
或 .prettier.config.js
和 .prettierrc.toml
文件。
// package.json
{
...
"prettier": {
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"semi": false
}
}
// .prettier.config.js
module.exports = {
printWidth: 80,
tabWidth: 2,
useTabs: false,
singleQuote: false,
semi: true,
}
命令行:
// package.json
// 采用.prettier.config.js配置时,命行令中要加 --config .prettier.config.js
{
"scripts": {
"format": "prettier --write \"src/**/*.+(js|jsx|css)\"",
// "format": "prettier --write --config .prettier.config.js \"src/**/*.+(js|jsx|css)\""
},
}
然后,执行 npm run format
即可。
**整合:**prettier 和 Git 整合。
安装:npm i lint-staged husky -D
配置:
// package.json
{
...
"lint-staged": {
"src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
"prettier --write --config .prettier.config.js \"src/**/*.+(js|jsx|css)\"",
"git add"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
}
添加 Router(路由)
CRA 并未规定特定的Router(路由)解决方案,但 React Router 是最受欢迎的 Router(路由) 解决方案。
import {
BrowserRouter as Router,
// HashRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import Home from '../home'
import About from '../about'
import Dashboard from '../dashboard'
function App () {
return (
<Router>
<div>
<ul>
<li> <Link to="/">Home</Link> </li>
<li> <Link to="/about">About</Link> </li>
<li> <Link to="/dashboard">Dashboard</Link> </li>
</ul>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/dashboard" component={Dashboard} />
</Switch>
</div>
</Router>
)
}
export default App
React Router中的组件主要分为三类:
路由器,例如<BrowserRouter>
和 <HashRouter>
<BrowserRouter>
使用常规URL路径。这些通常是外观最好的网址,但它们要求您的服务器配置正确。 具体来说,您的Web服务器需要在所有由React Router客户端管理的URL上提供相同的页面。
<HashRouter>
将当前位置存储在URL的哈希部分中,因此URL看起来类似于http://example.com/#/your/page
。 由于哈希从不发送到服务器,因此这意味着不需要特殊的服务器配置。
路由匹配器,例如 <Route>
和 <Switch>
渲染 <Switch>
时,它会搜索其子元素 <Route>
,以查找其路径与当前URL匹配的元素。当找到一个时,它将渲染该 <Route>
并忽略所有其他路由。这意味着您应该将 <Route>
包含更多特定路径(通常较长)的路径放在不那么特定路径之前。
导航,例如 <Link>
,<NavLink>
和 <Redirect>
添加样式
如何引入样式
**添加常规 CSS 文件。**即通过 import
的方式导入 .css
文件。
/**index.css**/
.app_link {
color: red
}
import './index.css'
function App() {
return <div>
<p className="app_link">Learn React</p>
</div>
}
export default App
添加 CSS Modules 样式表。
CRA 项目使用 [name].module.css
文件命名约定支持 CSS Modules
和常规 CSS 。 CSS Modules 允许通过自动创建 [filename]\_[classname]\_\_[hash]
格式的唯一 classname
来确定 CSS 的作用域。
/**index.module.css**/
.app_module_link {
color: orange;
}
import styles from './index.module.css';
function App() {
return <div>
<p className={styles.app_module_link}>Learn React</p>
</div>
}
export default App
最终渲染结果:
<div><p class="index_app_link__1pkYt">Learn React</p></div>
添加 Sass 样式表。
首先,安装:npm install node-sass -S
,然后,将文件扩展名改为 .scss
或 .sass
(建议用 .scss,书写格式更接近常规css)。
/**index.scss**/
.app_scss_link {
color: $my-color;
}
import './index.scss'
function App() {
return <div>
<p className="app_scss_link">Learn React</p>
</div>
}
export default App
使用css-in-js。
// index.js
import styled from 'styled-components'
const LinkP = styled.p`
color: deeppink;
`;
function App() {
return <div>
<LinkP>Learn React</LinkP>
</div>
}
export default App
最终渲染结果:
<div><p class="sc-bdnxRM kqYXav">Learn React</p></div>
Postcss预处理器
CRA 内嵌了压缩、自动添加浏览器前缀等插件,同时,你也可以按需要引入一些 Postcss 插件。如:postcss-apply
、postcss-css-variables
、postcss-px2rem-exclude
等。
安装:npm i postcss-px2rem-exclude -D
**配置:**修改 craco.config.js
,添加 postcss 插件配置
// craco.config.js
module.exports = {
style: {
sass: {
loaderOptions: {
additionalData: "$my-color: cyan;"
}
},
postcss: {
plugins: [
require("postcss-import")({
"path": "src/assets/css"
}),
require("postcss-preset-env")({
features: {
"custom-properties": {
preserve: false,
variables: {}
},
"nesting-rules": true
}
}),
require("postcss-apply")({}),
require("postcss-css-variables")({}),
require("postcss-px2rem-exclude")({
remUnit: 16,
exclude: /node_modules|folder_name/i
}),
]
},
},
}
结语
CRA 项目还可以结合 redux
管理页面状态。
除了 create-react-app
外,也可以选用 dva
、umi
等应用框架。
相比于 CAR 只是多了内置的 redux
和 redux-saga
,帮我们处理了数据流这方面的需求而已。如果只是想要达到这个效果的话,直接在 CAR 中增加 dva 的依赖也是可以做到的。
参考链接