React 代码规范

205 阅读7分钟

前言

在这篇文章中,我对 React 代码规范 进行了总结,分享出我认为的最佳实践,供大家参考。

代码书写规范

1. jsx 标签的书写规范

// 当标签没有子元素时,始终使用自闭合标签
<Footer data={data} />

// 当标签有子元素时,选择使用双标签闭合
<Footer
  bar={bar}
  data={data}
>
  <Button />
</Footer>

2. jsx 属性对齐方式

// 如果没有属性,在闭合标签前添加一个空格
<Footer />

// 如果是多个属性,直接属性换行对齐的方式,同时闭合标签是换行的
<Footer
  bar={bar}
  data={data}
/>

// 如果可以放在一行,放在一行上即可
<Footer bar={bar} />

// 如果是多行,采用缩进的书写方式
<Footer
  bar={bar}
  data={data}
>
  <Button />
</Footer>

3. 样式写法

(1) 引入外联样式,一经加载将全局有效,类名相同时样式之间会互相影响,造成全局污染(不采用)
// bad 🔴
{
  import React from 'react';
  import './index.less';

  const TestCom = () => (
    <div className='out-css'>
      <p className='text'>引入外联样式</p>
    </div>
  );

  export default TestCom;
}

(2) 引入外联模块化样式的类名不会全局污染
将css文件作为一个模块引入,这个模块中的所有css,只作用于当前组件;不会影响当前组件的后代组件;样式之间不会有冲突
// good ✅
{
  import React from 'react';
  import styles from './index.less';

  const TestCom = () => (
    const flag = true;

    {/* 无中划线的类名这样使用 */}
    <div className={styles.wrapTest}>
      {/* 有中划线的类名这样使用 */}
      <span className={styles['span-test']}>引入外联模块化样式</span>
      {/* 动态判断样式这样使用 */}
      <span className={`${flag ? styles['flag-true'] : styles['flag-false']}`}>引入外联模块化样式</span>
    </div>
  );

  export default TestCom;
}

4. 命名规则

基础规则:变量,类名,文件命名需要有实际含义(语意化),不能使用系统字段进行命名(关键字、保留字等)

// 组件名称使用大驼峰命名
import ReservationCard from './ReservationCard';

// 属性名称使用小驼峰命名
<Footer
  data={data}
  showStatus={showStatus}
  onClickSubmit={onClickSubmit}
/>;

// 自定义事件名称使用小驼峰命名
const onClickSubmit = () => {};

// 自定义函数,动词开头,明确函数意图
const sendEmailToUser = (user) => {... }; // good ✅
const emailUser = (user) => {... }; // bad 🔴

// 自定义常量命名全部大写,下划线分割,避免使用没有意义的命名(语意化)
const CHART_TYPE = 'line'; // 图表类型
const TIME_OUT = 1000 * 30; // 超时时间

// 自定义变量命名使用小驼峰或者下划线命名,不能使用脚本语言中保留的关键字、保留字,避免使用没有意义的命名(语意化)
const status = true; // good ✅
const showStatus = true; // good ✅
const show_status = true; // good ✅

const class = {}; // bad 🔴
const if = true; // bad 🔴

const a = 'Sunday'; // bad 🔴
const week = 'Sunday'; // good ✅

5. 公共方法函数必须有注释说明(author 非必写)

/**
 * @name getFunc
 * @desc 方法描述
 * @param {string, object}
 * @return bool
 * @author xxx
 */
export const getFunc = (param1, param1) => {
  return false;
};

6. key 属性设置

Key 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。

在使用 map() 渲染时,要在列表中使用 key,主要是为了复用列表元素,加快 diff 的过程,提高渲染的效率。

⚠️ 注意事项:

  • key 值一定要和具体的元素一一对应;
  • 尽量不要用数组的 index 去作为 key;(数组的改变,会导致索引对应的数组内容不正确)
// bad 🔴
{
  todos.map((todo, index) => <Todo {...todo} key={index} />);
}

// good ✅
{
  todos.map((todo, index) => <Todo {...todo} key={todo.id} />);
}

7. Hooks 相关

(1) 不要缺少 useEffect 依赖

默认情况下,它总是在每次重新渲染时运行。但这样就可能会导致不必要的渲染。我们可以通过给 useEffect 设置恰当的依赖数组来避免这些不必要的渲染。

⚠️ 注意事项:不是所有的依赖都必须放到依赖数组中。只有一种情况,需要把变量放到依赖数组中,那就是当该变量变化时,需要触发 useEffect 函数执行。而不是因为 useEffect 中用到了这个变量!

const Counter = () => {
  const [count, setCount] = useState(0);

  const showCount = (count) => {
    console.log('Count', count);
  };

  // bad  每次渲染之后都会执行 🔴
  useEffect(() => {
    showCount(count);
  });

  // good  第一次渲染完成之后运行,只执行一次 ✅
  useEffect(() => {
    showCount(666);
  }, []);

  // good  只有当变量count发生改变之后才会重新执行 ✅
  useEffect(() => {
    showCount(count);
  }, [count]);

  return <div>Counter: {count}</div>;
};

(2) 不要忘记清理副作用

所谓副作用,就是对函数之外造成影响,指那些会影响其他组件以及在渲染过程中无法完成的操作,如:定时器、事件监听、操作 dom 等。

const Counter = () => {
  const barRef = useRef<any>();
  const [count, setCount] = useState(0);

  const resizeEvent = () => {
    console.log('resizeEvent');
  };

  useEffect(() => {
    const myChart = echarts.init(barRef.current);
    myChart.setOption({});

    const timer = setTimeout(() => {
      setCount((count) => count + 1);
    }, 100);

    window.addEventListener('resize', resizeEvent);

    return () => {
      myChart.dispose(); // 销毁echarts实例
      clearTimeout(timer); // 清除定时器
      window.removeEventListener('resize', resizeEvent); // 移除事件监听
    };
  }, []);

  return <div>Counter: {count}</div>;
};

(3) 使用多个 Effect 实现关注点分离

使用 Hook 其中一个目的就是要解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题。

使用多个 effect,这会将不相关逻辑分离到不同的 effect 中。

const BusinessLogic = () => {
  const [count, setCount] = useState(0);
  const [status, setStatus] = useState(false);
  const initData = () => {};

  useEffect(() => {
    const showCount = () => {};

    initData();
    setCount(666);
    showCount();
    // do something...
  }, []);

  useEffect(() => {
    const handleStatusChange = () => {};

    initData();
    setStatus(true);
    handleStatusChange();
    // do something...
  }, []);
  // ...
};

(4) 只在最顶层使用 Hook,不要在循环、条件或嵌套函数中调用 Hook

不要在循环,条件或嵌套函数中调用 Hook,确保总是在你的 React 函数的最顶层以及任何 return 之前调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。

const BusinessLogic = () => {
  const [count, setCount] = useState(0);
  const [status, setStatus] = useState(false);

  // 🔴 不能在 Hook 调用之前 return
  if (!status) {
    return '状态不能为空!';
  }

  // 🔴 不要在条件语句中使用 Hook
  if (status) {
    useEffect(() => {
      setCount(666);
    }, []);
  }

  // ✅ 将条件判断放置在 effect 中
  useEffect(() => {
    if (status) {
      setCount(666);
    }
  }, []);

  // ✅ 保证 Hook 在最顶层使用
  if (!status) {
    return '状态不能为空!';
  }
};

8. 业务逻辑代码处理

(1) 适当的注释

对于复杂的业务逻辑代码,应该进行适当的注释,防止自己遗忘代码细节,同时也让其他同事能粗略看懂,提高代码的可维护性。

(2) 适当的抽离

对于复杂的业务逻辑代码,视情况而定,进行模块化,抽离成单独的函数方法或者组件,让主文件结构看起来更加清晰,提高代码的可读性。

9. 组件复杂度

(1) 逻辑拆分和抽象

如果一个组件做的事情太多,应依照一定的规则和条件,去适当提取一些逻辑,将其拆分为更小的组件。

代码行数并不是一个客观的衡量标准,更多是需要考虑责任划分和抽象。

(2) props 传参数量

如果超过 5 个 props,就该考虑是否拆分该组件。在某些情况下,这是需要对组件进行重构的标志。

⚠️ 注意:组件使用的 props 越多,重新渲染的理由就越多。

10. 一个文件声明一个组件

尽管可以在一个文件中声明多个 React 组件,但是最好不要这样做;推荐一个文件声明一个 React 组件,并只导出一个组件

代码格式化规范

对应编辑器插件 Prettier 配置

代码格式化工具 Prettier 中文官网地址 www.prettier.cn

1. 代码缩进 2 个空格符(tabWidth: 2)

const tabWidth = {
  a: 1,
  b: 2,
};

2. 使用单引号(singleQuote: true)

const singleQuote = {
  a: '1',
  b: '2',
};

3. 结尾添加分号(semi: true)

const a = 1;
const b = 2;

4. object 对象里面的 key 和 value 值和括号间加空格(bracketSpacing: true)

const a = { foo: 'bar' };
const fun = ({ foo: 'bar' }) => {};

5. 单行代码超过 80 个字符需做换行操作(printWidth: 80)

配置编辑器(VScode为例)

(1)创建一个 .prettierrc 文件,并配置规则,让编辑器和其他工具知道你正在使用 Prettier

{
  "tabWidth": 2,
  "printWidth": 80,
  "singleQuote": true,
  "semi": true,
  "bracketSpacing": true,
  "overrides": [
    {
      "files": ".prettierrc",
      "options": {
        "parser": "json"
      }
    }
  ]
}

(2)创建一个 .prettierignore 文件,让 Prettier CLI 和编辑器知道哪些文件不能格式化

**/*.svg
**/*.ejs
**/*.html
package.json
.umi
.umi-production
.umi-test
/dist
/build
/public

(3)在IDE中安装 Prettier-Code Formater 插件

(4)找到IDE中设置模块,搜索 format On Save,勾选上这个就可以了