怎么去创建一个新的 react 项目

最近有新的项目需求,突然想整理下新建项目的这个过程,供以后借鉴,或者给他人提供一些思路 ~

完整项目模板参考 Github 地址

技术调研和选型

技术调研和选型都应该从业务出发,用技术赋能业务。以我们的项目为例,这是一个 pc 项目,主要是对地图进行编辑(绘制图形等),对页面性能要求较高,供内部人员使用,无 seo 需求。最终确定使用的技术栈如下:

  • react17、Hooks
  • react-router v6
  • typescript
  • webpack 5
  • axios
  • mobx。数据管理,因为涉及较多的数据交互,使用 mobx 在性能上更胜一筹,模板代码也少一些。其他的选择有 redux、xstate
  • less、css module
  • ahooks。内置了很多工具 hooks,墙裂推荐 ~
  • 其他的一些必要依赖

包管理器

这个看个人喜好吧,选 yarn 或 npm 都挺好的。个人比较喜欢用 yarn,初始安装速度比 npm 快,具体对比可以参考 https://pnpm.io/zh/benchmarks

搭建完项目后就可以配置.npmrc.yarnrc文件,设置 npm 源为淘宝源,这样安装依赖的时候就不用手动指定了

# .yarnrc
registry https://registry.npmmirror.com/
# 或者建议换成公司内部的 npm 源

搭建项目

基于 create-react-app 脚手架快速搭建一个 使用 typescript 的 react 项目

yarn create react-app my-app --template typescript
# 或者
npx create-react-app my-app --template typescript

安装好后,需要先把所有必要的依赖都先安装上,注意区分开发环境的依赖和生产环境的依赖

接下来在项目模板的基础上完善项目结构,主要是调整 src 的目录结构

src
├─ assets
│  ├─ css          # 全局样式
│  ├─ icon
│  └─ image
├─ containers      # 页面组件
├─ components      # 公共组件
├─ helper          # 存放工具函数或者业务抽象函数
│  ├─ utils        # 工具函数
│  ├─ hooks        # 公共 hooks
│  ├─ service.ts   # 请求工具函数
├─ type            # 全局类型
├─ store           # 状态管理
└─ ...

规范配置

目的是统一代码编写格式和规范,助力团队协作,提高代码健壮性和可读性。按照后面的方法增加完配置文件后的目录结构如下:

├─ src
├─ .vscode   # vscode 编辑器配置
├─ .husky    # husky 配置
├─ .commitlintrc.js
# 代码检查
├─ .stylelintrc.js
├─ .eslintrc.js
# 代码格式化
├─ .prettierrc
├─ .editorConfig
├─ .gitignore
├─ .npmrc
├─ tsconfig.json

.vscode

现在 vscode 是前端最热门的编辑器,可以约定团队成员统一使用 vscode 编码,然后预设必要的编辑器配置,比如保存自动格式化代码、默认格式化工具采用 prettier 等。在 .vscode 下新建 settings.json

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode"
}

eslint

注意只是让 eslint 检查语法和发现错误而不是纠正格式,代码格式由 prettier 统一。可以让同事多注重下代码质量,而不是依赖格式化修正代码错误

yarn add eslint -D
yarn eslint --init
# 通过命令行交互生成 .eslintrc.js

.eslintrc.js 参考配置如下:

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
    node: true,
    jest: true,
  },
  extends: [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "plugin:@typescript-eslint/recommended",
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: "latest",
    sourceType: "module",
  },
  plugins: ["react", "@typescript-eslint"],
  rules: {
    "no-var-requires": 0,
  },
};

package.json 增加 script 命令,用于全局检查项目代码

"eslint": "eslint --fix src/**/*.{js,ts,jsx,tsx}",

需要配合 vscode 的 eslint 插件来使用

详细配置参考 https://eslint.org/docs/user-guide/configuring/

stylelint

类似于 eslintstylelint 可以帮助检查和修复 css/less/scss 代码

yarn add stylelint stylelint-config-standard -D

项目根目录下新增 .stylelintrc.js,配置参考如下:

module.exports = {
  extends: "stylelint-config-standard",
  customSyntax: "postcss-less",
};

增加 script 命令,便于全局检查样式文件:

"stylelint": "stylelint --fix src/**/*.{css,less}",

然后记得在 vscode 中安装 stylelint 插件

prettier

yarn add prettier -D

.prettierrc 参考配置如下:

{
  "tabWidth": 2,
  "singleQuote": true,
  "semi": true,
  "trailingComma": "all"
}

增加 script 命令,便于格式化项目代码:

"prettier": "prettier --write src/**/*.{js,ts,jsx,tsx,less,css}"

需要安装 vscode 的 prettier 插件,然后可以选择开启保存时自动格式化代码的功能

详细配置参考 https://prettier.io/docs/en/configuration.html

editorConfig

使用不同编辑器打开同一份文件,如果编辑器配置不统一,显示效果和输入内容很有可能不一致。EditorConfig 就主要用于统一代码编辑器编码风格

.editorConfig 配置参考

# https://editorconfig.org

# 已经是顶层配置文件,不必继续向上搜索
root = true

[*]
# 编码字符集
charset = utf-8
# 缩进风格是空格
indent_style = space
# 一个缩进占用两个空格,因没有设置tab_with,一个Tab占用2列
indent_size = 2
# 换行符 lf
end_of_line = lf
# 文件以一个空白行结尾
insert_final_newline = true
# 去除行首的任意空白字符
trim_trailing_whitespace = true

[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

详细配置参考 https://editorconfig.org/

Git 扩展

  • husky。操作 git 钩子的工具
  • lint-staged。本地暂存代码检查工具,可以让 husky 只检验 git 工作区的文件
yarn add husky lint-staged -D

package.json 追加以下配置

"scripts": {
  "prepare": "husky install",
},
"lint-staged": {
  "src/**/*.{js,jsx,ts,tsx}": [
    "prettier --write",
    "eslint --cache --fix",
    "git add"
  ],
  "src/**/*.{css,less}": [
    "stylelint --fix",
    "git add"
  ],
},
  1. 初始化 husky
yarn prepare
  1. 安装 commitlint。这个插件可以校验提交的 commit 信息是否符合规范,不符合则不可以提交
yarn add @commitlint/cli @commitlint/config-conventional -D
  1. 在根目录下创建 .commitlintrc.js
module.exports = {
  extends: ["@commitlint/config-conventional"],
};
  1. 然后执行以下命令,会自动在生成的 .husky 文件夹下新建两个钩子文件commit-msgpre-commit
npx husky add .husky/commit-msg 'yarn commitlint --edit "$1"'
npx husky add .husky/pre-commit 'yarn lint-staged'

文件内容如下(确保自动生成的文件内容跟以下的保持一致):

  • commit-msg
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo "========= 校验 commit-msg ======="
yarn commitlint --edit $1
  • pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo "========= 执行 lint-staged ======="
yarn lint-staged

在 git commit 之前会进入工作区文件的扫描,执行 prettier 脚本,修改 eslint 问题,校验 commit msg,通过后再提交到工作区

注意:以上脚本执行的路径要确保与 .git 同级,如果出现以下情况

- .git
- front
    - src
    - package.json

先调整 package.json

"scripts": {
  //...
  "prepare": "cd .. && husky install front/.husky",
},

然后执行 yarn prepare,重新生成 hook 脚本

npx husky add .husky/commit-msg 'yarn commitlint --edit "$1"'
npx husky add .husky/pre-commit 'yarn lint-staged'

修改文件内容如下

  • commit-msg
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo "========= 校验 commit-msg ======="
cd front
yarn commitlint --edit $1
  • pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo "========= 执行 lint-staged ======="
cd front
yarn lint-staged
  • commitizen【可选】。命令行交互辅助输入 commit msg
npm i -g commitizen
commitizen init cz-conventional-changelog --save --save-exact

之后在提交 commit 时运行 git cz

cz-customizable。英文看着不爽的话,可以用这个插件自定义 commitizen 中文配置,自行搜索和配置吧!

扩展 webpack

基于 react-app-rewired 扩展,这样后续也能享受到 react-scripts 的更新。当然如果是需要高度定制 webpack 配置的话,可以把配置文件导出来(eject)

alias

比较常规的路径映射有 @ --> src 如下:

import { utils } from "@/helper/utils"
  1. 借助 react-app-rewiredcustomize-cra 扩展 webpack 配置
yarn add react-app-rewired customize-cra

修改 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 文件

const { override, addWebpackAlias } = require("customize-cra");
const path = require("path");

module.exports = {
  webpack: override(
    // 配置alias
    addWebpackAlias({
      ["@"]: path.resolve(__dirname, "src"),
      ["components"]: path.resolve(__dirname, "src/components"),
      ["mock"]: path.resolve(__dirname, "src/mock"),
    })
  ),
};
  1. 需要先修改 tsconfig,确保在编译期间 ts 能正确映射到某个路径下的文件。正常情况下是直接配置 tsconfigpaths 就可以了,但是 CRA 脚手架执行项目(start)时会将其覆盖。可以通过在 tsconfig 引入 paths.json,避开这个问题
// tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "extends": "./paths.json",
  "include": ["src", "config"]
}

根目录新增 paths.json

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

然后尝试在组件中使用 @ 引入模块吧!

less + css module

css 预处理语言,其实 sass 和 less 都可以吧,也是看个人喜好,然后结合 css module 设置样式作用域。在 config-overrides.js 中追加配置如下:

yarn add less less-loader -D
// config-overrides.js
const { override } = require("customize-cra");
// 需要先安装 yarn add customize-cra-less-loader -D
const addLessLoader = require("customize-cra-less-loader");

module.exports = {
  webpack: override(
    // ...
    // 添加less解析器
    addLessLoader({
      lessOptions: {
        javascriptEnabled: true,
        sourceMap: false,
        // modifyVars: { '@primary-color': '#1DA57A' },
      },
    })
  ),
  // ...
};

之后在项目中使用,不过可能会报下面的错

Cannot find module ‘./index.module.less’ or its corresponding type declarations

需要在 react-app-env.d.ts 中追加 less 文件的声明:

declare module '*.less' {
 const content: { [className: string]: string };
 export default content;
}

设置全局 less 变量,新增 src/styles/global.less

// base variables
@font-size-base: 16px;
@font-size-lg: @font-size-base + 2px;
@font-size-sm: 12px;

// color
@color-green: #00b050;
@color-yellow: yellow;
@color-orange: orange;
@color-red: red;

// animation
@animation-duration-slow: 0.3s;
@animation-duration-base: 0.2s;
@animation-duration-fast: 0.1s;

// z-index list

更新 webpack 的 less 文件处理规则,需要安装 style-resources-loader

const { override, adjustStyleLoaders } = require("customize-cra");

module.exports = {
  webpack: override(
    //...
    adjustStyleLoaders((rule) => {
      if (rule.test.toString().includes("less")) {
        rule.use.push({
          loader: "style-resources-loader",
          options: {
            patterns: path.resolve(__dirname, "src/styles/global.less"),
            injector: "append",
          },
        });
      }
    })
  ),
};

环境配置

1.默认使用 dotenv 和 env 文件来配置环境变量。CRA 默认有开发(.env.development)和线上环境(.env.production),如果只有这两个环境需要配置,只需要在 src 目录下新建这两个文件,然后配置变量就行了,构建过程会自动加载环境变量,参考官方文档 https://create-react-app.dev/docs/adding-custom-environment-variables/#what-other-env-files-can-be-used

如果不只是这两个环境,可以在 src 下新建 env 文件夹,存放对应的环境配置文件,然后参考以下方式修改 package.json(不同环境对应不同的构建命令)

"scripts": {
    "buid:dev": "dotenv -e env/.env.dev react-app-rewired build",
    "build:prod": "dotenv -e env/.env.prod react-app-rewired build",
    // ...
}

记得安装 dotenv 插件

yarn add dotenv-cli -D

注意变量需要以 REACT_APP_ 开头,其余自定义的变量会被忽略。配置文件参考:

// .env.dev
# 自定义环境变量
REACT_APP_ENV=dev
# 路由前缀
REACT_APP_BASEURL="/"
# 静态资源路径前缀
PUBLIC_URL="/"

2.可以通过 cross-env 和不同的执行命令注入环境变量,调整 package.json,示例如下:

"scripts": {
    "start": "cross-env REACT_APP_NODE_ENV=dev react-app-rewired start",
    "build:test": "cross-env REACT_APP_NODE_ENV=test react-app-rewired build",
    "build": "cross-env REACT_APP_NODE_ENV=prod react-app-rewired build",
    "test": "react-app-rewired test",
    "eject": "react-app-rewired eject"
},

之后可以在代码中通过区分 REACT_APP_NODE_ENV 来加载对应的环境变量。参考以下配置文件:

const configMap: Record<Env, IConfig> = {
  [Env.Dev]: { ...commonConfig, ...devConfig },
  [Env.Test]: { ...commonConfig, ...testConfig },
  [Env.Prod]: { ...commonConfig, ...prodConfig },
};
const currentEnv = process.env.REACT_APP_NODE_ENV ?? Env.Dev;
export const envConfig = configMap[currentEnv as Env];

这个是相比 dotenv 要多出的一个前置步骤

增加编译进度条

yarn add chalk@^4 progress-bar-webpack-plugin -D
// config-overrides.js
const chalk = require('chalk');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
// ...
module.exports = {
  webpack: override(
    // ...
    addWebpackPlugin(
      new ProgressBarPlugin({
      format: `  :msg [:bar] ${chalk.green.bold(':percent')} (:elapsed s)`,
    }),
  ),
}

增加项目信息输出

// config-overrides.js
const path = require("path");
const exec = require("child_process").execSync;

process.env.REACT_APP_NAME = require("./package.json").name;
process.env.REACT_APP_VERSION = require("./package.json").version;
process.env.REACT_APP_BUILD_TIME = new Date().toLocaleString("zh-CN", {
  hour12: false,
});
process.env.REACT_APP_GIT_COMMIT = exec('git rev-parse --short=8 HEAD')
  .toString()
  .trim();

增加基础函数

// helper/utils/index.ts
/**
 * 显示项目配置信息,比如项目名、版本等
 */
export function showProjectInfo() {
  log.capsule(
    `${process.env.REACT_APP_NAME}`,
    `v${process.env.REACT_APP_VERSION}`
  );
  log.primary(`Build Time:  ${process.env.REACT_APP_BUILD_TIME}`);
  log.primary(`Last Commit: ${process.env.REACT_APP_GIT_COMMIT}`);
}

然后修改入口文件

// src/index.tsx
import { showProjectInfo } from "./utils";
// ...
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root"),
  () => {
    // show project info in console
    showProjectInfo();
  }
);

最终的 config-overrides.js 文件参考 https://github.com/GitHubJackson/react-spa-template/blob/main/config-overrides.js

包体积分析

借助 webpack-bundle-analyzer 插件

yarn add webpack-bundle-analyzer -D

扩展 webpack 配置

// config-overrides.js
const BundleAnalyzerPlugin =
  require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// ...
  addWebpackPlugin(
    // ...
    new BundleAnalyzerPlugin(),
  ),

修改 package.json,增加相关的命令行

"scripts": {
  // ...
  "build:analyzer": "react-app-rewired build --stats && webpack-bundle-analyzer build/bundle-stats.json -m static -r build/bundle-stats.html -O",
}

执行命令后,浏览器打开 build/bundle-stats.html 文件就可以看到项目依赖的体积总览

项目实践

路由配置

基于 react-router v6,借助 React.lazy 和动态 import 实现路由懒加载

yarn add react-router-dom

根目录新增 routes/index.ts,路由配置如下:

import React from "react";

const LazyLogin = React.lazy(() => import("@/components/login"));
const LazyHome = React.lazy(() => import("@/components/home"));

export const routes = [
  {
    /**
     * 登录页面
     */
    path: "/login",
    component: LazyLogin,
  },
  {
    /**
     * 主页
     */
    path: "/home",
    component: LazyHome,
  },
];

App.tsx 中新增路由代码

import React, { Suspense } from "react";
import {
  BrowserRouter as Router,
  Routes,
  Route,
  Navigate,
} from "react-router-dom";
import { routes } from "./routes";
import "antd/dist/antd.min.css";
import "./App.css";

function App() {
  const FallbackComponent = null;
  return (
    <Router>
      <Suspense fallback={FallbackComponent}>
        <Routes>
          {routes.length > 0 &&
            routes.map((router) => {
              return (
                <Route
                  path={router.path}
                  element={<router.component />}
                  key={router.path}
                />
              );
            })}
          <Route path="*" element={<Navigate to="/login" />} />
        </Routes>
      </Suspense>
    </Router>
  );
}

export default App;

在布局组件中,可以通过以下的方式插入子页面

import { Outlet } from "react-router";
// ...
<Content className={styles["content"]}>
  <Outlet />
</Content>;

状态管理

mobx 可以给 react 增加响应式更新的能力,减少多余组件渲染的开销。参考代码如下:

import { createContext } from "react";
import { makeAutoObservable } from "mobx";

// NOTE 如果要限制,得先确保所有代码只通过 action 修改 store 属性
// 否则渲染过程有打印 warning log,会造成一定的性能损耗
// configure({ enforceActions: "always" });

// Model the application state.
class CommonStore {
  count = 0;
  constructor() {
    makeAutoObservable(this);
  }
  add = () => {
    this.count++;
  };
  reduce = () => {
    this.count--;
  };
  get getCount() {
    return "count is " + this.count;
  }
}
export const commonStore = new CommonStore();
export const rootStore = {
  commonStore,
};

export const storeContext = createContext(rootStore);
export const StoreProvider = storeContext.Provider;

index.tsx 中引入 store

//...
import { rootStore, StoreProvider } from "./store";

ReactDOM.render(
  <React.StrictMode>
    <StoreProvider value={rootStore}>
      <App />
    </StoreProvider>
  </React.StrictMode>,
  document.getElementById("root")
);

还可以写个公共 hooks,供函数组件调用

// useStore.ts
import { useContext } from "react";
import { storeContext } from ".";

export const useStore = () => useContext(storeContext);

在组件中使用

import React from "react";
import { useLocalStore, useObserver } from "mobx-react-lite";
import { useStore } from "@/helper/hooks/useStore";
import styles from "./index.module.less";

function Check() {
  const { commonStore } = useStore();
  const { add, reduce } = commonStore;
  // 定义局部 store,仅限当前组件使用
  const todo = useLocalStore(() => ({
    // state
    title: "Click to toggle",
    done: false,
    // action
    toggle() {
      todo.done = !todo.done;
    },
    // getter
    get emoji() {
      return todo.done ? "😜" : "🏃";
    },
  }));

  return useObserver(() => (
    <div className={styles["container"]}>
      <button onClick={add}>add</button> {commonStore.count}
      <button onClick={reduce}>reduce</button>
    </div>
  ));
}

export default Check;

webpack 需要扩展配置来支持 mobx-react

mobx-react 有用到装饰符,CRA 目前还没有内置的装饰器支持,需要增加相应的 babel 插件。如果是只使用 React Hook 和mobx-react-lite 开发,则不需要配置

const {
  override,
  addDecoratorsLegacy,
  disableEsLint,
  //...
} = require("customize-cra");

module.exports = {
  webpack: override(
    addDecoratorsLegacy(),
    disableEsLint()
    //...
  ),
};

disableEsLint 是用于防止报错 Parsing error: Using the export keyword between a decorator and a class is not allowed. Please use 'export @dec class' instead

接口使用规范

这个主要看个人编码喜好吧,我自己定义的接口使用规范涉及三层:

  1. 基于 axios 创建 service 工具函数,在这里设置一些基本配置,比如 api url 前缀、拦截器逻辑、通用的状态码处理、…
// helper/utils/service.ts
import axios from "axios";
import { envConfig } from "@/config";

export const service = axios.create({
  baseURL: envConfig.baseUrl,
  timeout: 100000,
});

// Add a request interceptor
service.interceptors.request.use(
  function (config) {
    // Do something before request is sent
    return config;
  },
  function (error) {
    // Do something with request error
    return Promise.reject(error);
  }
);

// Add a response interceptor
service.interceptors.response.use(
  function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
  },
  function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    return Promise.reject(error);
  }
);
  1. 接口函数,封装具体接口的处理逻辑、异常处理、…
// services/index.ts
import { service } from "@/helper/utils/service.ts";

export async function getUserList(): Promise<IUserData[]> {
  const res = await service.get("xxx");
  if (res.code !== 200) {
    message.error(`message: ${res.msg}, error: ${res.errorMsg}`, 2.5);
    return;
  }
  // NOTE obj2CamelCase 用于转换下划线为驼峰命名,因为前端统一使用驼峰命名
  return obj2CamelCase(res.data) as IUserData[];
}
  1. useRequest(ahooks),业务层消费接口
import { getUserList } from "./services/index.ts";
// 获取用户列表
const { data } = useRequest(getUserList, {
  manual: false,
  onSuccess: (data) => {
    //...
  },
});

新建页面

示例代码如下:

import React from 'react';
import { RouteComponentProps } from 'react-router-dom';

const : React.FC<RouteComponentProps> = props => {

  return (
    <div>

    </div>
  );
}

export default ;

可以结合 vscode 定制页面模板代码(preferences > user snippets),快速导入。比如新增上述模板代码,先选择 typescript react 进入 json 文件,新增以下配置:

{
  //...
  "quickly hook": {
    "prefix": "reacthook",
    "body": [
      "import React from 'react';",
      "import { RouteComponentProps } from 'react-router-dom';",
      "",
      "const $1: React.FC<RouteComponentProps> = props => {",
      "",
      "\treturn (",
      "\t\t<div>",
      "\t\t\t",
      "\t\t</div>",
      "\t);",
      "}",
      "",
      "export default $1;"
    ],
    "description": "quick reacthook"
  }
  //...
}

之后在 tsx 文件中,输入 reacthook 就可以通过提示导入模板代码

单元测试

前端可以通过 Jest 做一些逻辑和组件的单元测试,场景包括 UI 组件库、utils、公共 hooks 等

CRA 其实已经集成了这个能力,我们可以通过编写满足条件的测试用例文件,来快速使用这个能力

Jest 将使用以下任何流行的命名约定来查找测试文件:

  • __tests__ 文件夹中带有 .[js|ts] 后缀的文件
  • 带有 .test.[js|ts] 后缀的文件
  • 带有 .spec.[js|ts] 后缀的文件

这些文件可以放在 src 下任意文件夹中,建议统一放置于 src/__tests__ 中。编写完之后可以通过 yarn test 运行测试,Jest 将以 watch(观察) 模式启动。 每次保存文件时,它都会重新运行测试

更多内容可以参考 https://www.html.cn/create-react-app/docs/running-tests/

sentry

sentry 可以帮助我们监控和收集页面的异常

yarn add @sentry/react @sentry/tracing

然后在 src/index.tsx 中引入即可

import * as Sentry from "@sentry/react";
import { Integrations } from "@sentry/tracing";

Sentry.init({
  dsn: "xxx", // TODO 这个需要在 sentry 中注册后获取,create project 时注意选择 react 应用
  integrations: [new Integrations.BrowserTracing()],

  // 我们建议在生产中调整此值,或使用 tracesSampler 进行更精细的控制
  tracesSampleRate: 1.0,
});

之后所有未处理的异常都会被 Sentry 自动捕获,为了验证配置的有效,可以尝试在 App.tsx 中加入这个组件

<button onClick={methodDoesNotExist}>Break the world</button>

然后启动应用,之后可以在 project > 问题 中看到对应的错误信息

sentry 默认是纯英文,可以进入 User settings 修改 language,改为 Simplified Chinese 然后刷新页面

添加 ErrorBoundary

如果您使用的是 React 16 或更高版本,则可以使用 Error Boundary 组件将组件树内部的 Javascript 错误自动发送到 Sentry,并设置回退 UI

Mock

前端经常需要先 mock 数据开发,我们可以通过搭建一个 mock server 来帮助我们快速高效地 mock 接口数据

yarn add koa koa-router koa-bodyparser mockjs nodemon -D

在 src 下新建一个 mock 文件夹,然后在 mock 文件夹下创建 server.js 文件

const Koa = require("koa");
const router = require("koa-router")();
const bodyParser = require("koa-bodyparser");
const testData = require("./test.js");

const app = new Koa();
app.use(bodyParser());
app.use(router.routes());

router.get("/test", async (ctx, next) => {
  ctx.body = testData;
  await next();
});

// error-handling
app.on("error", (err, ctx) => {
  console.error("server error", err, ctx);
});

app.listen(3001);

新增 test.js,编写测试接口。接口定义建议先和后端对齐

const Mock = require("mockjs");

const data = Mock.mock({
  "list|1-10": [
    {
      "id|+1": 1,
    },
  ],
});

module.exports = data;

然后修改 package.json

"mock": "nodemon src/mock/server.js",

在实际开发的时候,就可以多开一个终端运行 mock server,然后在前端页面直接访问 mock 的接口就可以了

最后

我建立了一个仓库 react-spa-template,集成了以上的能力,欢迎 comment ~

持续更新,更多玩法探索中…