前端优秀编码技巧

持续更新...

多级解构

将部分变量从对象中解构出来的时候,涉及到多层对象的解构,可以直接一次完成,不需要分两次。

bad ❌

javascript 复制代码
const { state, dispatch } = usePageReducer();
const { currStep } = state
console.log(currStep, 'currStep')

after good ✅

javascript 复制代码
const { state: { currStep }, dispatch } = usePageReducer();
console.log(currStep, 'currStep')

// 当state为可选时可以加上默认值 {}
const { state: { currStep } = {}, dispatch } = usePageReducer();

includes 代替多个判断

在遇到多个条件相等判断时,可以用includes 代替多个||,这样代码简洁,且其可以对NaN进行相等比较。

bad ❌

js 复制代码
if(x === 'undefined' || x === 'true' || (typeof x === 'number' && isNaN(x))) {
    .....
}

after good ✅

js 复制代码
const list = [false, '', true, false, undefined, null, NaN, {}, 0]
if(list.includes(x)) {
    ....
}

console.log(list.includes('')); // true
console.log(list.includes(false)); // true
console.log(list.includes(undefined)); // true
console.log(list.includes(null)); // true
console.log(list.includes(NaN)); // true
console.log(list.includes(0)); // true
console.log(list.includes({})); // false

if else换成三元运算

但是注意不要滥用三元运算符,过多的三元运算符嵌套不利于可读性性,所以遇到复杂的条件判断还是要考虑使用if/else、switch 拆分或者map映射条件。

bad ❌

javascript 复制代码
const testFunc = (a, b) => {
  if(a > 0) {
     return a
  }
  else{
     return 0
  }
}

after good ✅

javascript 复制代码
const testFunc = (a, b) => {
 	return a > 0 ? a : 0
}

filter(Boolean)过滤

当想过滤所有转化为boolean值后为false的值时, 可以采用filter(Boolean)简写的形式

bad ❌

js 复制代码
const arr = [1, '1', '', 0, null, undefined, NaN, false]
const usefulArr = arr.filter(item => !!item) // [ 1, '1' ]

after good ✅

js 复制代码
const arr = [1, '1', '', 0, null, undefined, NaN, false]
const usefulArr2 = arr.filter(Boolean) // [ 1, '1' ]

Object.is 比较一致

Object.is 相较于 === 的区别是其可以比对 -0和+0以及NaN间的比较结果,常用于方便的比较NaN的情况

===

js 复制代码
const checkSame = (value1, value2) => {
  return value1 === value2
}
checkSame({}, {}) // false
checkSame('test', 'test') // true
checkSame(NaN, NaN) // false
checkSame(-0, +0) // true

Object.is

js 复制代码
const checkSame = (value1, value2) => {
  return Object.is(value1, value2)
}
checkSame({}, {}) // false
checkSame('test', 'test') // true
checkSame(NaN, NaN) // true
checkSame(-0, +0) // false

return 无用条件

提前将后面不需要用到的判断条件从方法中return出去,这样可以减少判断层级和次数。

bad ❌

javascript 复制代码
const testFunc = (a, b) => {
  if(a && b === 1) {
    return a + b
  }
  if(a && b === 2) {
    return b
  }
}

after good ✅

javascript 复制代码
const testFunc = (a, b) => {
  if(!a) return
  if(b === 1) return a + b
  if(b === 2) return b
}

合理使用可选链运算符

当可以确定数据对象的类型格式时,切忌不要过多的使用可选链运算符,防止不必要的判断

bad ❌

javascript 复制代码
interface Response {
  data: {
    detail?: {
      value: number
    }
  }
}
const res: Response = await getData()
const value = res?.data?.detail?.value

after good ✅

ts 复制代码
interface Response {
  data: {
    detail?: {
      value: number
    }
  }
}
const res: Response = await getData()
// 去掉了 data 的可选链运算取值
const value = res?.data.detail?.value

多次用到的变量提前解构

bad ❌

javascript 复制代码
const num = obj.data + 10
return num > 0 ? num : obj.data * 10

after good ✅

javascript 复制代码
const { data } = obj
const num = data + 10
return num > 0 ? num : data * 10

&& 使用要谨慎

我们往往会使用 && 作为一个判断条件,但是不然如果前面的值有时是number类型时,他可能并不会在条件不满足的时候返回false,可以使用!!进行转化成boolean 或者使用三元运算符 代替。

bad ❌

javascript 复制代码
const arr = []
return arr.length && 'a'

after good ✅

javascript 复制代码
const arr = []
return !!arr.length && 'a'
or 
return arr.length ? 'a' : false

多用reduce

当我们遇到一些对象数组转化时,可以使用reduce很好的减少代码量,也可以让多次循环变成一次循环。

bad ❌

javascript 复制代码
const disabledFieldData = ['enterpriseData#mytime#1', 'enterpriseData#mytime#2']
const func = (fatherKey: string) => {
  const fieldKey = `enterpriseData#${fatherKey}#`;
  // 执行了2次循环
  return disabledFieldData.filter((item) => item.fieldKey.includes(fieldKey))
    .map(item => Number(item.fieldKey.replace(fieldKey, '')));
}
func('mytime')

after good ✅

javascript 复制代码
const disabledFieldData = ['enterpriseData#mytime#1', 'enterpriseData#mytime#2']
const func = (fatherKey: string) => {
  const fieldKey = `enterpriseData#${fatherKey}#`;
  // 一次循环搞定
  return disabledFieldData.reduce((pre, cur) => {
    if (cur.fieldKey.startsWith(fieldKey)) {
      pre.push(Number(cur.fieldKey.replace(fieldKey, '')));
    }
    return pre;
  }, [] as number[]);
}
func('mytime')

try...catch 多用finally

对于无论是 try还是catch都要执行的公共语法可以放在finally内。

bad ❌

javascript 复制代码
let a = false
try {
  const data = await api(...)
  ...
  a = true
  
}.catch(err => {
  console.log(err)
  a = true
})

after good ✅

javascript 复制代码
let a = false
try {
  const data = await api(...)
  ...
}.catch(err => {
  console.log(err)
}).finally(() => {
   a = true
})

简写 ?? 和 ||

bad ❌

javascript 复制代码
a = a ?? 'default'
b = b || 'default'

after good ✅

javascript 复制代码
a ??= 'default'
b ||= 'default'

书写请求公共方法时要防止重复请求

以下两种错误方式第一种没有任何缓存可言,第二种在同一时间请求入参相同时依然无法避免重复发送请求

bad ❌

ts 复制代码
// 获取菜鸟地址列表
funtion getStateCityList(key) {
  return getStateCityListReq({ divisionId: key })
}
// or 
let cacheValue = {}
async funtion getStateCityList(key) {
  if(cacheValue[key]) return cacheValue
  const res = await getStateCityListReq({ divisionId: key })
  cacheValue[key] = res.data || []
  return cacheValue[key]
}

采用缓存 Promiseresolve方法 或者 缓存整个return的Promise可以实现防止重复请求

after good ✅

ts 复制代码
// 菜鸟地址缓存
export const CacheAddressMap = new Map();
// 获取菜鸟地址列表(缓存 防止重复发送请求)
funtion getStateCityList(key) {
    if (CacheAddressMap.has(key)) {
      return CacheAddressMap.get(key)
    }
    const CacheKey = `${key}-pending`;
    const pendingList = CacheAddressMap.get(CacheKey);
    if (pendingList) {
      return new Promise((resolve) => {
        pendingList.push(resolve);
      })
    }
    CacheAddressMap.set(CacheKey, []);
    const paddingPromise = new Promise((resolve) => {
      getStateCityListReq({ divisionId: key }).then((res) => {
        const resData = res.data || [];
        CacheAddressMap.set(key, resData);
        CacheAddressMap.get(CacheKey)?.map((CacheResolve) => CacheResolve(resData));
        CacheAddressMap.delete(CacheKey);
        resolve(resData);
      });
    });
    return paddingPromise
}

多类型条件收窄(TS)

当有一个变量有多种类型的可能性时我们往往习惯使用as直接断言但是这是不可取的,可以使用is方法类型收窄

bad ❌

ts 复制代码
function test(response: Response1 || Response2){
    if(response.type === 'test1') {
        (response as Response1).list.forEach(item => {
            .... // 类型 Response1 的处理
        })
    }else {
        // 类型 Response2 的处理
    }
}

after good ✅

ts 复制代码
function checkResponseType(res: Response1 || Response2): res is Response1 {
    if(response.type === 'test1') return true
    return false
}
function test(response: Response1 || Response2){
    if(checkResponseType(response)) {
        response.list.forEach(item => {
            .... // 类型 Response1 的处理
        })
    }else {
        // 类型 Response2 的处理
    }
}

如果一些属性是一种类型里有,一种类型里没有可以使用 in 语法

bad ❌

ts 复制代码
type girl = { read: () => void };
type boy = { write: () => void };
 
function test(person: Fish | Bird) {
    if ((person as girl).read) {
        person.read();
    }else {
        return animal.write();
    }
}

after good ✅

ts 复制代码
type girl = { read: () => void };
type boy = { write: () => void };
 
function test(person: Fish | Bird) {
    if ("read" in person) {
        person.read();
        return
    }
    animal.write();
}

用enum代替重要常量对象(TS)

对于一些多处会用到的公共常量状态标识等,我们建议分别对key和值定义枚举,提高编码规范和复用性。

bad ❌

javascript 复制代码
export const statusStep: : Record<string, '0' | '1' | '2'> = {
  init: '0',
  'waiting_submit_enterprise_data': '1',
  FAIL: '2',
  SUCCESS: '2'
}

after good ✅

javascript 复制代码
export enum EOnboardingStatus {
  /** 初始化状态,进入用户签约页面 */
  INIT = 'INIT',
  /** 等待提交 */
  WAIT_SUBMIT = 'waiting_submit_enterprise_data',
  /** 入驻失败 */
  FAIL = 'FAIL',
  /** 审核成功 */
  SUCCESS = 'SUCCESS',
}

export enum EOnboardingStep {
  START = '0',
  MID = '1',
  END = '2' 
}

export const statusStep: Record<EOnboardingStatus, EOnboardingStep> = {
  [EOnboardingStatus.INIT]: EOnboardingStep.START,
  [EOnboardingStatus.WAIT_SUBMIT]: EOnboardingStep.MID,
  [EOnboardingStatus.FAIL]: EOnboardingStep.END,
  [EOnboardingStatus.SUCCESS]: EOnboardingStep.END,
};
相关推荐
恋猫de小郭33 分钟前
Flutter Widget IDE 预览新进展,开始推进落地发布
android·前端·flutter
jingling5552 小时前
【Vue3 实战】插槽封装与懒加载
前端·javascript·vue.js
萌萌哒草头将军7 小时前
🚀🚀🚀 Openapi:全栈开发神器,0代码写后端!
前端·javascript·next.js
萌萌哒草头将军7 小时前
🚀🚀🚀 Prisma 爱之初体验:一款非常棒的 ORM 工具库
前端·javascript·orm
拉不动的猪8 小时前
SDK与API简单对比
前端·javascript·面试
runnerdancer8 小时前
微信小程序蓝牙通信开发之分包传输通信协议开发
前端
BillKu8 小时前
Vue3后代组件多祖先通讯设计方案
开发语言·javascript·ecmascript
山海上的风8 小时前
Vue里面elementUi-aside 和el-main不垂直排列
前端·vue.js·elementui