基于Create React App打造代码规范化的React+Ts项目
Create React App 是一个官方支持的创建 React 单页应用程序的方法。它提供了一个零配置的现代构建设置。
而在实际的项目开发中,我们通常希望可以自行配置一些参数。如:webpack 的alias、typescript 的path、eslint 的rules等。本文记录了基于 create-react-app 打造 react+ts 的过程遇到的各种问题,以及对应的解决方法。
- npm: 6.9.0 
- node: v12.2.0 
- 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 (不推荐)
npm run eject (不推荐)它会将所有配置文件和传递依赖项(Webpack,Babel,ESLint等)复制到项目中,以便你可以完全控制它们。除 eject 之外的所有命令仍然有效,但它们将指向复制的脚本,以便你可以调整它们。
注意:这是单向操作。一旦你 eject ,你就不能回去了!
CRA 通过升级其中的 react-scripts 包来升级 CRA 的特性,而使用了 eject 命令,就再也享受不到 CRA 升级带来的好处了。因为 package 中的 scripts 的命令已经指向了项目中的配置文件,而不是指向 react-scripts 包。
2. react-app-rewired + customize-cra
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"
3. @craco/craco
craco 是另一款类似 react-app-rewired 的功能。
- 安装: - npm i @craco/craco -S
- 配置:在项目根目录下添加 - craco.config.js配置文件。更看更多配置- /* 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: undefinedNODE_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 文件- .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配置
方法一:Create React App 官方给出的解决方案
- 添加境变量 - EXTEND_ESLINT为- true。
- 修改 - 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 AppReact 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等应用框架。
- dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。 - 相比于 CAR 只是多了内置的 - redux和- redux-saga,帮我们处理了数据流这方面的需求而已。如果只是想要达到这个效果的话,直接在 CAR 中增加 dva 的依赖也是可以做到的。
- umi 是蚂蚁金服的底层前端框架,中文可发音为乌米,是一个可插拔的企业级 react 应用框架。umi 以路由为基础的,支持类 next.js 的约定式路由,以及各种进阶的路由功能,并以此进行功能扩展,比如支持路由级的按需加载。然后配以完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求,目前内外部加起来已有 50+ 的插件。 
参考链接
最后更新于
这有帮助吗?