前端优秀编码技巧

持续更新...

多级解构

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

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,
};
相关推荐
Boilermaker199213 分钟前
【Java EE】SpringIoC
前端·数据库·spring
中微子25 分钟前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上102440 分钟前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
芬兰y1 小时前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁1 小时前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry1 小时前
Fetch 笔记
前端·javascript
拾光拾趣录1 小时前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟1 小时前
vue3,你看setup设计详解,也是个人才
前端
Lefan1 小时前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson1 小时前
青苔漫染待客迟
前端·设计模式·架构