React 组件开发规范

使用 Typescript

Typescript 能够提供很好的代码检查功能,配合 vscode 在开发阶段就能第一时间洞悉语法错误。

使用 Function component

只在少数特殊情况下使用 class component

不使用 React.FC

// bad
export const Button: React.FC<ButtonProps> = (props) => {
  ...
}

// good
export const Button = (props: ButtonProps) => {
  ...
}

// good
export const Button = ({type, size, ...restProps}: ButtonProps) => {
  ...
}

原因出处:

  • https://github.com/facebook/create-react-app/pull/8177
  • https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components/

不使用 prop-types

虽然 prop-typestypescript 不是完全的对等,但是完全没有必要在 typescript 项目中使用 prop-types

不使用 defaultProps

// bad
Button.defaultProps = {
  type: 'default',
  size: 'middle',
};

// good
export const Button = ({type = 'default', size = 'middle', ...restProps}: ButtonProps) => {
  ...
}

原因出处:

  • https://github.com/facebook/react/pull/16210
  • https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/default_props/

不使用 enum

type 更方便进行扩展,书写时也能方便进行提示。

// bad
enum ButtonSize {
  LARGE = 'large',
  MIDDLE = 'middle',
  SMALL = 'small',
}

// good
type ButtonSize = 'large' | 'middle' | 'small';

// Extensible:
type ExtendedButtonSize = ButtonSize | 'mini';

不使用第三方库

  • 第三方库的代码质量参差不齐不可控
  • 滥用第三方库会导致打包体积变大
  • 新手可能因为依赖第三方库而能力得不到提高

如果特殊情况下,不得不使用第三方库,必须经过 Review

使用 CSS 变量

css 变量提供了更加动态化的样式调整能力,也让组件的样式调整变得更加简洁优雅。

// bad
.ud-button {
  color: #7284fb;
}
// bad
.ud-button {
  color: var(--ud-color-primary);
}

// good
.ud-button {
  color: $button-color-primary;
}
// variables.scss
$button-color-primary: var(--ud-button-color-primary, var(--ud-color-primary)); // 默认态按钮主要色

使用 CSS 变量接口

开发组件时,需要考虑组件样式的扩展性,使用 var 对外暴露 CSS 变量接口,禁止写死属性。

// bad
$button-color-primary: #7284fb; // 默认态按钮主要色

// good
$button-color-primary: var(--ud-button-color-primary, var(--ud-color-primary)); // 默认态按钮主要色

使用组件拆分

必须对组件进行拆分,以增加代码的可阅读性和可维护性。下面是一个 Input 组件:

const renderPrepend = () => {...}
const renderPrefix = () => {...}
const renderInput = () => {...}
const renderClearBtn = () => {...}
const renderCount = () => {...}
const renderPasswordBtn = () => {...}
const renderSuffix = () => {...}
const renderAppend = () => {...}
...

使用 NativeProps

所有组件统一使用 NativeProps 来支持 style, classNamechildren 属性。

import { NativeProps } from '../../utils';

export type ButtonProps = {
  ...
} & NativeProps;

使用 name

统一使用 name 来作为组件的唯一标志符,不使用 activeKey 的原因是方便书写。

export type ItemProps = {
  name: string; // 唯一标志符
  // ...
} & NativeProps;

编写测试

最佳实践是将测试文件命名为 componentName.test.js,这样会很方便查找。

推荐使用 Jest + React Testing Library,enzyme 已不推荐使用。

使用代码校验

应该强制在 commit 之前做 lint 检查,可以在 package.json 中添加 precommit 脚本

"scripts": {
    "precommit": "npm run lint",
    "lint": "npm run lint:js && npm run lint:style",
    "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
    "lint:style": "stylelint --fix \"src/**/*.less\" --syntax less",
    ......
}

或者使用 lint-stage 只校验本次修改涉及的代码。

"lint-staged": {
  "apps/**/*.{js,jsx,ts,tsx}": [
    "eslint --ext '.js,.jsx,.ts,.tsx'"
  ],
  "packages/**/*.{js,jsx,ts,tsx}": [
    "eslint --ext '.js,.jsx,.ts,.tsx'"
  ]
},

同一个组件的所有文件放在一起

将同一个组件的所有相关文件放在同一个目录下,css 和 js 放在同级目录下,比如 Comment 组件可以这样:

/components/Comment/CommentForm/index.less
/components/Comment/CommentForm/index.tsx
/components/Comment/CommentList/index.less
/components/Comment/CommentList/index.tsx
/components/Comment/CommentItem/index.less
/components/Comment/CommentItem/index.tsx
/components/Comment/index.less
/components/Comment/index.ts
/containers/Comment/index.less
/containers/Comment/index.tsx

并且在 /components/Comment/index.ts 里 export 所有子组件。

区分有无状态组件

React 官方建议:多写无状态组件;组件最小粒度化;组件只是数据的管道工!

无状态组件

相同输入,相同输出,没有副作用; 负责 render markup and styles, read data from props and invoke callbacks from props. 这种组件应该放在**/components**目录下。

有状态组件

相同输入,不一定相同输出,还可能修改外部的值; 负责 data fetching, dispatch actions, 直接与 redux 等交互。几乎没有 HTML 标签,除了一个容器 div。 这种组件应该放在**/containers**目录下。

https://stackoverflow.com/questions/43414254/difference-between-component-and-container-in-react-redux

使用 hooks

在 React 16.8 版本之后,逻辑的复用,无论是 container 还是 component 都可以复用逻辑,这部分代码应该使用自定义 hooks,并且放在**/hooks**目录下。

核心:state 最小粒度,是 React 的最佳实践。

最终 src 目录结构

  • assets
  • /components
    • /Comment
      • /CommentForm
        • index.less
        • index.tsx
      • /CommentList
        • index.less
        • index.tsx
      • /CommentItem
        • index.less
        • index.tsx
      • index.less
      • index.ts
  • /containers
    • /Comment
      • index.less
      • index.tsx
  • /hooks
  • /layouts
  • /locales
  • /models
    • comment.ts
  • /pages
  • /services
    • comment.ts
  • /utils
    • utils.js
    • utils.test.js
  • app.ts
  • global.less
  • global.ts
  • manifest.json

参考

https://onesignal.com/blog/effective-typescript-for-react-applications/ https://www.codeinwp.com/blog/react-best-practices/

© 2022  Arvin Xiang
Built with ❤️ by myself