前端编码最佳实践

87 阅读3分钟

1.三元表达式和if使用场景

// bad
if(n > 0) {
  fn1()
} else {
  fn2()
}

// good
n > 0 ? fn1() : fn2()

// bad 三元嵌套三元不利于可读性
let message = (age < 3) ? 'Hi, baby!' :
  (age < 80) ? 'Hello!' :
  'What an unusual age!';
// good
if (age < 3) {
  message = 'Hi, baby!';
} else if (age < 80) {
  message = 'Hello!';
} else {
  message = 'What an unusual age!';
}

2.魔法值的问题

// bad 魔法值举例
if(flag === '5'){
  .......
}
if (businessType === 101){
  .......
}
//good 使用枚举
const BusinessTypeEnum = {
  SYSTEM: 0, // 系统
  CRM: 1, // CRM
  JXC: 2, // JXC
  UNKNOWN: 404, // 未知对象类型
  CUSTOMER_MANAGEMENT: 100, // 客户管理
  CUSTOMER: 101, // 客户
  CUSTOMER_FOCUS: 102, // 重点客户
  CUSTOMER_DEAL: 103, // 成交客户
  CUSTOMER_FOLLOW: 104, // 跟进客户
  CUSTOMER_PUBLIC: 105 // 客户公海池
}

if (businessType === BusinessTypeEnum.CUSTOMER){
  .......
}

采用枚举的另外一个好处,当某个值因为需求迭代需求变更时,我们只需要在枚举中将该值替换,并不需要在全局搜素替换。

3.属性传递的问题

属性传递问题是一种常见的不良习惯,它发生在将属性从一个组件传递到多层嵌套的子组件时。这可能导致性能问题和代码可读性降低。理想情况下,应该尽量避免将属性传递超过2层。下面是一个示例:

// 父组件
function ParentComponent() {
  const data = 'Some data';

  return (
    <ChildComponent data={data} />
  );
}

// 子组件
function ChildComponent({ data }) {
  return (
    <GrandchildComponent data={data} />
  );
}

// 孙子组件
function GrandchildComponent({ data }) {
  // 使用数据
  return <div>{data}</div>;
}

在上面的示例中,data属性通过多个嵌套层级传递,这可能导致性能问题和可读性问题。解决这个问题的方法之一是使用React的上下文(context)来共享数据,或者重新组织组件结构。

4.if嵌套的问题

以下代码是4层if-else嵌套的代码,即使已经将具体逻辑封装到一个个函数里边了,但还是看着费劲,更别说未封装的情况!

function fn(data) {
  if (data !== null) {
    if (data.num >= 5) {
      fn2()
      if (data.flag) {
        fn3()
      } else{
        fn3Err()
      }
    } else if (data.num >= 0) {
      fnErr2()
    } else {
      fnErr3()
    }
  } else {
    fnErr()
  }
}

改进方法:先处理坏情况,剩下的就是好情况,从而把横向嵌套层数降为1

function fn(data) {
  if (data === null) return fnErr() // 1级坏情况:data为null
  if (data.num < 0) return fnErr3() // 2级坏情况
  if (data.num < 5) return fnErr2() // 2级坏情况
  
  // 剩下的就是1级好情况了,即data !== null && data.num >= 5
  fn2()
  if (!data.flag) return fn3Err() // 3级坏情况
  fn3()
  // 或合并成一行: data.flag ? fn3() : fn3Err()
}

5.引入本地文件、三方文件

  • 引入文件的顺序,一般依赖的库写在前面,本地文件写在后面,同类型的可以空格区分
  • 避免冗余的引入
// js库
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { Box, Typography, Alert, TableContainer, TableBody, TableCell, TableHead, TableRow, Paper, Table } from '@mui/material';
import { useFormik } from 'formik';
import * as yup from 'yup';
import FormatMessage from 'intl-service';
import { useTheme } from '@mui/material/styles';
import EastIcon from '@mui/icons-material/East';
// 本地组件
import UAutocomplete from 'views/components/UAutocomplete';
import SelectList from 'views/components/SelectList';
// 本地js方法
import { fieldType } from 'utils/const';
import { underscoreToCamel } from 'utils/utils';
// API
import { GetCDPAttributes } from 'api/contacts';

6.减少重复计算

React组件可能会在不同时间点重新渲染,如果不小心,某些工作可能会在每次渲染时都重复执行,这会降低性能。为了避免这种情况,可以使用useMemouseCallback来记忆化操作,以便它们不会在每次渲染时重新计算。以下是一个示例:

function List({ items }) {
  // 不使用useMemo - 每次渲染都会重新过滤
  const filteredItems = items.filter(item => item.active);

  // 使用useMemo - 只在items发生变化时重新过滤
  const filteredItems = useMemo(() => items.filter(item => item.active), [items]);
}

7.业务逻辑与组件逻辑分离

React中,尽量将业务逻辑与UI组件逻辑分开,以提高代码的可读性和可维护性。将业务逻辑提取到独立的服务文件或模块中,以使组件保持简单。以下是一个示例:

// 不良示例 - 业务逻辑混杂在组件中
function UserProfile() {
  const user = getUserData(); // 从API获取用户数据

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

// 良好示例 - 业务逻辑分离
function UserProfile() {
  const user = useUserData(); // 从独立服务获取用户数据

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}