前端代码优化方法积累

97 阅读2分钟

前言

将项目中遇到的代码优化方法进行了一个整理,使代码增加可维护性和可读性

1、优化for循环

传统写法

let array = []
for (let item of this.chartData) {
  let params = {
     name: item.name,
     type: 'line',
     data: item.value,
  }
  array.push(params)
}

优化写法

let array = this.chartData.data.map((item)=>{
    return {
      name: item.name,
      type: 'line',
      data: item.value,
    }
})

2、魔鬼字符串

传统写法

<template>
  <div>
    <el-button v-if="roleId !== 2" @click="handleClick">新增</el-button>
    <el-button v-if="roleId !== 2" @click="handleClick">编辑</el-button>
    <el-button v-if="roleId === 3" @click="handleClick">删除</el-button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      roleId: null
    }
  },
  created() {
    this.roleId = localStorage.getItem('roleId')
    if (this.roleId === 2) {
      this.getNormalList()
    }
    if (this.roleId === 3) {
      this.getSuperManageList()
    }
    if (this.roleId === 1) {
      this.getManageList()
    }
  },
  methods: {
    handleClick() {
      // 普通员工
      if (this.roleId === 2) {
        return
      }
    },
    getManageList() {

    },
    getNormalList() {

    },
    getSuperManageList() {

    }
  }
}
</script>

优化写法

<template>
  <div>
    <el-button v-if="roleId !== rolesConfig.normalRole" @click="handleClick">新增</el-button>
    <el-button v-if="roleId !== rolesConfig.normalRole" @click="handleClick">编辑</el-button>
    <el-button v-if="roleId === rolesConfig.superManagerRole" @click="handleClick">删除</el-button>
  </div>
</template>

<script>
const rolesConfig = {
  normalRole: 2,
  managerRole: 1,
  superManagerRole: 3
}
export default {
  data() {
    return {
      roleId: null,
      rolesConfig
    }
  },
  created() {
    this.roleId = localStorage.getItem('roleId')
    if (this.roleId === rolesConfig.normalRole) {
      this.getNormalList()
    }
    if (this.roleId === rolesConfig.superManagerRole) {
      this.getSuperManageList()
    }
    if (this.roleId === rolesConfig.managerRole) {
      this.getManageList()
    }
  },
  methods: {
    handleClick() {
      // 普通员工
      if (this.roleId === rolesConfig.normalRole) {
        return
      }
    },
    getManageList() {

    },
    getNormalList() {

    },
    getSuperManageList() {

    }
  }
}
</script>

常用的消除魔术字符串的方法,其实就是把它写成一个变量。这样就可以消除耦合,以后我们只要维护一处代码配置就可以了。当然,你还可以通过import方式将config作为全局配置项供所有模块消费。

3、巧用JS隐式类型转换

// 字符串转数字代码对比 

const price = parseInt('32'); //传统方式
const price = Number('32'); //传统方式

const price = +'32'; //新方式

// 日期转换为时间戳代码对比 

const timeStamp = new Date().getTime(); //传统方式
const timeStamp = +new Date(); //新方式

//布尔转数字新方式
 console.log(+true); // Return: 1
 console.log(+false); // Return: 0

// 各种类型转布尔代码对比 

const isTrue = Boolean('') //传统方式
const isTrue = !!'' //新方式

const isTrue = Boolean(0) //传统方式
const isTrue = !!0 //新方式

const isTrue = Boolean(null) //传统方式
const isTrue = !!null //新方式

const isTrue = Boolean(undefined) //传统方式
const isTrue = !!undefined //新方式

// 数字转string代码对比
const num = 1;

const str = num.toString(); //传统方式
const str = num + ''; //新方式

4、导出git转post请求,防止链接拼接参数过多丢失

传统写法

import qs from 'querystring'

function exportData () {
  const params = {
    url: 'xxx',
    title: 'xxxx'
  }

  window.location.href = `/frs/ui/comparison/export/picture?${qs.stringify(params)}`
}

转换为post写法

/**
 * @desc 使用post方式来导出文件
 * @param {Object} params 导出的参数
 * @param {String} url 导出的请求地址
 */
function postExportFn = (params, url) => {
  var form = document.createElement('form')
  form.style.display = 'none'
  form.action = url
  form.method = 'post'
  document.body.appendChild(form)

  for (var key in params) {
    var input = document.createElement('input')
    input.type = 'hidden'
    input.name = key
    input.value = params[key]
    form.appendChild(input)
  }

  form.submit()
  form.remove()
}

5、命名

  • 业务模块文件夹名称和 vue 文件名称驼峰大写,比如 WarnPush、WarnPush.vue。
  • js 文件名称驼峰小写,比如 warnPush.api.js。
  • 路由名称使用驼峰小写,比如 /warnPush。
  • 方法名称动名词命名,比如 refreshTable。
  • 事件方法名称 handle 开头,比如 handleSearch。

6、v-if 和 v-for 不要用在同一个元素上。

<!-- bad -->
<ul>
  <li v-for="user in users" v-if="shouldShowUsers" :key="user.id">{{ user.name }}</li>
</ul>

<!-- good -->
<ul v-if="shouldShowUsers">
  <li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>

7、职责单一

任何时候尽量是的一个函数就做一件事情,而不是将各种逻辑全部耦合在一起,提高单个函数的复用性和可读性。比如:每个页面都会在加载完成时进行数据的请求并展示到页面。

// bad
methods: {
  getList1() {
    // to do ...
  },
  getList2() {
    // to do ...
  }
},
created() {
  this.getList1()
  this.getList2()
},

// good
methods: {
  // 将全部的请求行为聚合在init函数中
  init() {
    this.getList1()
    this.getList2()
  },
  getList1() {
    // to do ...
  },
  getList2() {
    // to do ...
  }
},
created() {
  this.init();
}

8、Prop 名大小写

在声明 prop 的时候,其命名应该始终使用 camelCase,而在模板中应该始终使用 kebab-case

// bad
export default {
  props: {
    'greeting-text': String,
  },
};

// good
export default {
  props: {
    greetingText: String,
  },
};
<!-- bad -->
<welcome-message greetingText="hi" />

<!-- good -->
<welcome-message greeting-text="hi" />

9、将一类方法封到对应hooks中,优化代码可读性

将同类方法和字段归类为对应hooks方法 ,调用hooks方法,代码可读性和维护性增加

// bad 命名和方法不归类,面条式写法

onMounted(async () => {
  await getMaterialList()
  await getMaterialsInfoList()
  await init()
})
let materialDataHas = ref({})
let form = reactive({
    ***: ''
  })
let $form = ref(null)
let index = ref(null)
let materialList = ref([])
let materialsInfoList = ref([])
const getMaterialList = async () => {
    try {
      let { data } = await action.****({})
      materialList.value = data
    } catch (error) {
      console.log(error)
    }
}
const getMaterialsInfoList = async (materialsTypeId, warehouseId) => {
    try {
      let { data } = await action.****({ ***
      })
      materialsInfoList.value = data
    } catch (error) {
      console.log(error)
    }
}
/** 类别确认  */
const confirmTreeAction = (item) => {
    form.***: = item.***
}
/** 选择名称确认  */
const confirmPickerAction = (item) => {
    form.***: = item.***
}
const init = () => {    
}

// good 将同类方法和字段归类为对应hooks方法 ,调用hooks方法,代码可读性和维护性增加

// 表格相关方法
const { materialDataHas, form, $form, index, confirmTreeAction, confirmPickerAction, init } = formDataMake()
// 物资下拉相关方法
const { materialList, materialsInfoList, getMaterialList, getMaterialsInfoList } = getMaterialsAction()
onMounted(async () => {
  await getMaterialList()
  await getMaterialsInfoList()
  await init()
})
/**
 * @author 
* @desc 物资相关方法
*/
function getMaterialsAction () {
  let materialList = ref([])
  let materialsInfoList = ref([])
 const getMaterialList = async () => {
    try {
      let { data } = await action.****({})
      materialList.value = data
    } catch (error) {
      console.log(error)
    }
}
const getMaterialsInfoList = async (materialsTypeId, warehouseId) => {
    try {
      let { data } = await action.****(***)
      materialsInfoList.value = data
    } catch (error) {
      console.log(error)
    }
}
  return {
    materialList,
    materialsInfoList,
    getMaterialList,
    getMaterialsInfoList
  }
}
/**
 * @author 
* @desc 表单赋值相关方法
*/
function formDataMake () {
  let materialDataHas = ref({})
  let form = reactive({
    ***: ''
  })
  let $form = ref(null)
  let index = ref(null)
  /** 类别确认  */
  const confirmTreeAction = (item) => {
    form.***: = item.***
  }
  /** 选择名称确认  */
  const confirmPickerAction = (item) => {
    form.***: = item.***
  }
  const init = () => {    
  }
  return {
    materialDataHas,
    form,
    $form,
    index,
    confirmTreeAction,
    confirmPickerAction,
    init
  }
}

10、索引优化代码

eg: 需求是星期回显,传 1 回显 星期一,传 7 回显 星期日,其他类推,无效回显空字符串;类似的需求还有回显月份 // bad 使用switch或者 if else实现

function previewWeek(i){
    switch(i){
        case 1:
            return '星期一'
            break;
        case 2:
            return '星期二'
            break;
        case 3:
            return '星期三'
            break;  
        case 4:
            return '星期四'
            break;  
        case 5:
            return '星期五'
            break;  
        case 6:
            return '星期六'
            break;  
        case 7:
            return '星期日'
            break;
        default:
            return ''
    }
}

// good 1 数组+索引优化代码

function previewWeek(i){
    return  i>0 && i<8 ?'星期'+['一','二','三','四','五','六','日'][i-1]:''
}

// good2 map优化代码

function previewWeek(i){
    let weeksMap = new Map([
        [1, '一'],
        [2, '二'],
        [3, '三'],
        [4, '四'],
        [5, '五'],
        [6, '六'],
        [7, '日']
    ]);
    return weeksMap.get(i)?'星期'+weeksMap.get(i):''
}

11、includes 优化代码

eg:我们来实现一个身份认证方法,通过传入身份Id返回对应的验证结果 // bad 常规写法,if else

function verifyIdentity(identityId){
    if(identityId==1 || identityId==2 || identityId==3 || identityId==4){
        return '你的身份合法,请通行!'
    }else{
        return '你的身份未知,警告!'
    }
}

// good includes + 三元

function verifyIdentity(identityId){
    return [1,2,3,4].includes(identityId)?'你的身份合法,请通行!':'你的身份未知,警告!'
}

12、switch语句优化

// bad 常规switch写法

let color = "red"
function printBlackBackground(){
    console.log('black')
}
function printRedBackground(){
    console.log('red')
}

function printBlueBackground(){
    console.log('blue')
}

function printGreenBackground(){
    console.log('green')
}

function printYellowBackground(){
    console.log('yellow')
}
switch(color) {
    case 'black':
        printBlackBackground();
        break;
    case 'red':
        printRedBackground();
        break;
    case 'blue':
        printBlueBackground();
        break;
    case 'green':
        printGreenBackground();
        break;
    default:
        printYellowBackground();
}

// good 用对象形式建立key-value映射关系

let color = "red"
let  colorMap = {
    black: printBlackBackground,
    red: printRedBackground,
    blue: printBlueBackground,
    green: printGreenBackground,
    yellow: printYellowBackground
}

colorMap[color]? colorMap[color]() : printYellowBackground()

13、默认值优化

// bad 常规写法

function request(options){
    let method = options.method?options.method:'GET'
    let data = options.data?options.data:{}
    //...
}

// good

function request(options){
    let opt = Object.assign({
        method:'POST',
        data:{}
    },options)
    return opt
}

14、单个 if 语句优化策略

// bad

if(a && b){
    c()
}

// good

a && b && c()

15、多个 else-if 带返回值 优化策略

// bad

function getPosition(direction){
    if(direction == "left"){
        return "左"
    }else if(direction == "right"){
        return "右"
    }else if(direction == "top"){
        return "上"
    }else if(direction == "bottom"){
        return "下"
    }else{
        return "未知"
    }
}

// good

function getPosition(direction){
    return ({
        left:"左",
        right:"右",
        top:"上",
        bottom:"下"
    })[direction] || "未知"
}

16、多个 else-if 执行不同方法 优化策略

// bad

let role = 'admin' //模拟登录接口返回的角色
document.querySelector('#btn').addEventListener( 'click' , function(){
    if(role == 'admin'){
        console.log('管理员点击此按钮执行的业务逻辑')
    }else if(role == 'subAdmin'){
        console.log('子管理员点击此按钮执行的业务逻辑')
    }else if(role == 'mall'){
        console.log('商场角色点击此按钮执行的业务逻辑')
    }else if(role == 'parkingLot'){
        console.log('停车场角色点击此按钮执行的业务逻辑')
    }
})

// good

let role = 'admin' //模拟登录接口返回的角色

let btnPermissionsControl = {
    admin:function(){
        console.log('管理员点击此按钮执行的业务逻辑')
    },
    subAdmin:function(){
        console.log('子管理员点击此按钮执行的业务逻辑')
    },
    mall:function(){
        console.log('商场角色点击此按钮执行的业务逻辑')
    },
    parkingLot:function(){
        console.log('停车场角色点击此按钮执行的业务逻辑')
    }
}
document.querySelector('#btn').addEventListener( 'click' , btnPermissionsControl[role] )

17、多个 if 嵌套优化策略

eg: 后端大哥说了,给你返回的数据里面的 如果有 userInfo字段,并且userInfo下面有hobby字段并且有值就显示 hobby里面的内容,否则页面 hobby这一块不显示

let result = {
    status:200,
    codeMsg:'success',
    data:{
        userInfo:{
            age:18,
            hobby:['敲代码','打篮球']
        }
    }
}

// bad

if(result.data){
    if(result.data.userInfo){
        if(result.data.userInfo.hobby){
            if(Array.isArray(result.data.userInfo.hobby)){
                if(result.data.userInfo.hobby.length){
                    //遍历 result.data.userInfo.hobby 进行渲染显示
                }
            }
        }
    }
}

// good1 用 && 进行优化

//成功拿到数据了 result
if ( result.data && result.data.userInfo && 
    result.data.userInfo.hobby && 
    Array.isArray(result.data.userInfo.hobby) && 
    result.data.userInfo.hobby.length ) 
{
    //遍历 result.data.userInfo.hobby 进行渲染显示
}

// good2 采用 try catch 策略

//成功拿到数据了 result
try {
   if(result.data.userInfo.hobby.length){
       //遍历 result.data.userInfo.hobby 进行渲染显示
   }
} catch (error) {
   
}

// good3 optional chaining优化

if(result?.data?.userInfo?.hobby?.length){
    //遍历 result.data.userInfo.hobby 进行渲染显示
}

18、利用ES6剩余参数...取你所想取的数据

// 包含后端不需要字段的参数对象 b和d
const params = {
    a:1,
    b:2,
    c:3,
    d:4
}
// 后端大哥说了,我只要a字段和c字段,其他多余字段给我后端,会报错,于是你就重新组装
// 没有级别的组装
delete params.b
delete params.d

// 青铜级别组装
const param = {
    a:params.a,
    c:params.c
}

// 钻石级别组装
const {a,c} = params
const param = {a,c}

// 王者级别组装
const {b,d,...param} = params // b和d字段是你需求排除的字段
// params:{a:1,c:3}

19、数组剩余参数...的巧用

const arr = [1,2,3,4,5]
const [a,b,...param] = arr // 排除 a和b对应的索引的值(即1和2) ,但是不能随心所欲的排除,比如排除 1和5 取[2,3,4]
param //[3,4,5]
// 如果想排除 1和5 取[2,3,4] 也是有方法的
arr.filter(i=>![1,5].includes(i)) // [2,3,4]

20、数组去重

1Array.from(new Set(oldArr))
2、[...new Set(arr)]

总结:

  • 尽量使用箭头函数
  • 尽量使用模板字符串
  • 尽量使用解构赋值
  • 尽量使用ES6语法,会发现代码会少很多

参考:

juejin.cn/post/684490…