Skip to content

代码优化

  • 增加代码可读性,增加代码可维护性
  • 封装公共方法的时候,要保证方法的通用性,尽量不要把业务的一些逻辑写在里面
  • 熟悉array的api也是很有必要的

消除多级if嵌套

ts
import { ref } from 'vue'

const count = ref(0)
const isActive = ref(false)

function test() {
  if (count.value < 5)
    return
  if (isActive.value)
    return

  add()
}

function add() {
  count.value++
  // 其他逻辑
}
ts
function checkStatus() { 
  if (isLogin()) { 
    if (isVip()) { 
      if (isDoubleCheck()) { 
        done() 
      }
      else { 
        throw new Error('不要重复点击') 
      } 
    }
    else { 
      throw new Error('不是会员') 
    }
  }
  else { 
    throw new Error('未登录') 
  } 
} 

function checkStatus() { 
  if (!isLogin()) 
    throw new Error('未登录') 

  if (!isVip()) 
    throw new Error('不是会员') 

  if (!isDoubleCheck()) 
    throw new Error('不要重复点击') 

  done() 
}
  • 在函数体中,先把否定的条件提前,return掉,后面只处理正面的逻辑。好处就是,写的时候思维不容易分散,写起来更清晰。
  • 这就是卫语句风格。

可选链简化判断和调用

ts
if ( 
  store.getters 
  && store.getters.userInfo 
  && store.getters.userInfo.menus 
) { 
  // 逻辑...//
}

if (store?.getters?.userInfo?.menus) { 
  // 逻辑...//
}

// 函数调用
props.onChange && props.onChange(e) 

props?.onChange?.(e) 

函数拆分

ts
function checkGameStatus() { 
  if (remaining === 0
    || (remaining === 1 && remainingPlayers === 1) 
    || remainingPlayers === 0) { 
    quitGame()
  } 
} 

function isGameOver() { 
  return ( 
    remaining === 0
    || (remaining === 1 && remainingPlayers === 1) 
    || remainingPlayers === 0
  ) 
} 

function checkGameStatus() { 
  if (isGameOver()) { 
    quitGame() 
  } 
} 
  • 代码逻辑复杂,可以按逻辑拆分成多个函数,方便维护。

函数传参优化

ts
function getMyInfo(name, age, gender, address, phone, email) { 
  // ...
}

// 行参封装成对象,对象函数内部解构
function getMyInfo(options) { 
  const { name, age, gender, address, phone, email } = options 
  // ...
} 

getMyInfo( 
  { 
    name: '鸽鸽', 
    age: 18, 
    gender: '男', 
    address: '新世界', 
    phone: '123456789', 
    email: '123456789@email.com'
  } 
) 

include 代替 ||

ts
import { ref } from 'vue'

const type = ref(0)

function test() {
  if (type === 1 || type === 2 || type === 3) { 
    console.log('ok') 
  } 

  if ([1, 2, 3].includes(type.value)) { 
    console.log('ok') 
  } 
}
  • 可以用includes消除||,后续添加新类型,只需要在数组中添加即可。
ts
import { ref } from 'vue'

type Type = 1 | 2

const type = ref<Type>()

const typeMap: Record<`${Type}`, () => void> = {
  1: add,
  2: mise,
}

function add() {
  console.log('add')
}

function mise() {
  console.log('mise')
}

function test(val: Type) {
  const fn = typeMap[val]
  fn?.()
}
ts
// switch case
function statusMap(status: string) { 
  switch (status) { 
    case 'success': 
      return 'SuccessFully'
    case 'fail': 
      return 'failed'
    case 'danger': 
      return 'dangerous'
    case 'info': 
      return 'information'
    case 'text': 
      return 'texts'
    default: 
      return status 
  } 
} 

// if else
function statusMap(status: string) { 
  if (status === 'success') 
    return 'SuccessFully'
  else if (status === 'fail') 
    return 'failed'
  else if (status === 'danger') 
    return 'dangerous'
  else if (status === 'info') 
    return 'information'
  else if (status === 'text') 
    return 'texts'
  else return status 
} 

// 使用映射进行优化
const STATUS_MAP = { 
  success: 'SuccessFully', 
  fail: 'failed', 
  warn: 'warning', 
  danger: 'dangerous', 
  info: 'information', 
  text: 'texts'
} 

function statusMap(status: string) { 
  return STATUS_MAP[status] ?? status 
} 
  • switch语法在javascript中,看起来层级很深,完全可以用映射代替
  • 可以用对象来代替多级if,后续添加新类型,只需要在对象中添加即可。
  • 也可以用 new Map(),道理是一样的

熟练使用array api

ts
const list = [0, 1, 2, 3]

const isAdd = ref(false)

async function test() {
  const userList = list.filter(Boolean) // [1, 2, 3]
  const userObj = userList.map(item => ({
    id: item,
    name: `${item} - ${item}`,
  }))

  const params = isAdd.value ? userList : userObj
  const requestFn = isAdd.value ? addUser : editUser

  await requestFn(params)
}
  • 用array 的 api,每个变量单独写出来,不要揉到一个for循环里面,代码可读性更好
  • 合理使用三目运算符,也能提高可读性
  • 代码换行分块
  • MDN Array

操作符

js
const obj = {}

const test = obj.a?.b // undefined

let val = null
val = val ?? 'default' // default
// 等同于
val = (val === undefined || val === null) ? 'default' : val
// 等同于
val ??= 'default'

// 同理还有`&&=`, `||=`, `??=`, `+=`, 等等

// `?.`
const obj2 = { a: { b: 1 } }
const val2 = obj2.a?.b // 1

// 函数调用
const fn = val || (() => 2)
const val3 = fn?.() // 2
  • ?? 操作符,可以判断一个值是否为null或者undefined,如果是,则返回后面的值。
  • ?. 可选链操作符,用来读取一个数据中的某一个属性,如果属性不存在,则返回undefined。也可以用来调用函数, 比如fn?.(),函数不存在,则返回undefined

promise

async/await

ts
async function test() {
  const num = await test2()
  console.log(num) // 6
}

async function test2() {
  const num = await test3()
  return num + 1
}

function test3() {
  // `number` 是Promise的返回值,通过泛型传入
  return new Promise<number>((resolve) => {
    setTimeout(() => {
      resolve(5)
    }, 1000)
  })
}
  • 异步操作,使用async/await,可以避免回调地狱。
  • 什么情况下只能用new Promise,那就是需要拿到一个callback内部的数据的时候,这个callback调用不一定是同步的,比如setTimeoutonload等。

try/catch

ts
async function test() {
  try {
    const data = await fetchFn()
    // 这里相当于promise.then里

    // 这里可以手动throw一个错误,会执行下面catch 里面的代码
    throw new Error('error')
  }
  catch (error) {
    // 这里相当于promise.catch里
    console.log(error)
  }
  finally {
    // 这里相当于promise.finally里
    console.log('finally')
  }
}

async function fetchFn() {
  return axios.get('/api')
}
  • async/awaittry/catch配套使用

try/catch 返回值探索

ts
function test() {
  try {
    return 1
  }
  catch (error) {
    return 2
  }
  finally {
    // eslint-disable-next-line no-unsafe-finally
    return 3
  }
  return 4
}

test() // 3
ts
function test() {
  try {
    return 1
  }
  catch (error) {
    return 2
  }
  finally {
    console.log('finally')
  }
  return 4
}

test() // 1
  • try/catch在函数中return的返回值,建议精读文档

Released under the MIT License.