【Vue】Vue中分支控制语句优化

122 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

假设有如下list,我们使用 v-for 将其渲染到页面,并且希望在点击不同项时会执行不同的事件。

list:[
{title:'点击执行事件A'},
{title:'点击执行事件B'},
{title:'点击执行事件C'}
]

要实现上述需求,最常见的做法就是为元素绑定 click 事件 handleClick,然后将 title 属性作为参数传入 handleClick 中,接着使用 if-else 来判断要执行的是哪个事件。

<ul>
    <li v-for="item in list" 
        :key="item.title"
        @click="handleClick(item.title)">
    {{ item.title }}
    </li>
</ul>
handleClick(title){
    if(title === '点击执行事件A'){
        A();
    } else if(title === '点击执行事件B'){
        B();
    } else{
        C();
    }
}

上面这种方式,虽然能实现需求,但是假如当 title 的值多起来的时候,其可读性就会降低很多,毕竟满屏的 if-else 谁看了都会觉得眼花,为了提高其可读性,我们可以使用另一种方法 switch 语句 来实现需求。

handleClick(titile){
    switch(title){
        case '点击执行事件A':
            A();
            break;
        case '点击执行事件B':
            B();
            break;
        case '点击执行事件C':
            C();
            break;
    }
}

使用 switch 确实使得代码变得好了那么一丢丢,但是还是不够,当条件多起来的时候,满屏的 case 还是让人看起来有点吃力,而且每个 case 都要带上一个 break,假设有 100 个 case ,那我们就重复写了 100 个 break

这样不大好,所以我们进一步思考,在上面这个需求中,list 中的元素和元素所对应的事件是一对一的关系,也就是说,list中的第一个元素对应着事件A,第二个元素对应着事件B,第三个元素对应着事件C。我们往跳一层,可以发现,这种关系可以抽象成对象中的键值对 , 以list为例子,我们将每个元素中 title 属性的值作为键名 key,将其对应的事件作为值 value,创建一个对象,进而我们可以将 handleCLick 改为如下形式:

hanldeClick(title){
 const methodsTree = {
     点击执行事件A:() => { /*事件A逻辑*/ },
     点击执行事件B:() => { /*事件B逻辑*/ },
     点击执行事件C:() => { /*事件C逻辑*/ },
 }
 // 执行事件 
 methodsTree[title] && methodsTree[title](); 
}

考虑到会有同值不同键的情况,我们可以把事件抽离出去,将 handleClick 变成下面这样:

hanldeClick(title){
 const methodsTree = {
     点击执行事件A:this.methodA,
     点击执行事件B:this.methodB,
     点击执行事件C:this.methodC,
 }
 // 执行事件 
 methodsTree[title] && methodsTree[title](); 
}

这样子看起来似乎是已经让代码清晰了很多了,但是我们不妨停下来再仔细想想,我们从 if-elseswitch 再到 methodsTree 是为了干什么? 是为了提高代码的可读性 呀,那什么是可读性? 就是当我们看到list的代码时,就已经能很明确的知道它每个元素所对应的事件了,而不同再跑到 handleClick 中逐个条件的去翻找它们的对应关系。 所以,我们转变个角度,从 list 的代码入手,将其修改成下面这种格式:

list:[
    {
        title:'点击执行事件A',
        methodName:'methodA'
    },
    {
        title:'点击执行事件B',
        methodName:'methodB'
    },
    {
        title:'点击执行事件C',
        methodName:'methodC'
    }
]

然后 handleClick 接收的参数就不再是单单一个 title 属性了,而是整个 item 了,当然,methodA\methodB\methodC 要先定义好。

handleClick(item){
   this[item.methodName] && this[item.methodName]();  // 执行对应事件
}

到这一步了,代码的可读性已经够强了吧。

补充: 上面的方法都是写在 methods 里面的,这样很不容易管理,我们可以将方法抽离出来写在一个 .js 文件中,然后通过 import 导入,但是这样做的话,handleClick 的代码就要变动一下了:

import * as moduleOne from './moduleOne.js' // 导入方法

handleClick(item){
    moduleOne[item.methodName] && moduleOne[item.methodName]().call(this);
}

从上面的代码可以看出,最后使用了 call() 来更改 this 的指向指向当前 vue 实例,为什么调用 methods 中的方法不需要使用 call(this) 呢?有两点原因:

  1. vue 中,methods 里的方法会被 vue 通过 bind() 方法绑定 this 的指向为当前 vue 实例,以后就算通过 call()apply() 也是无法修改的 ,所以调用 methods 中的方法无需使用 call(this) ,不然就有点画蛇添足了;
  2. 当方法或者函数写在 export 外面时,就需要使用 call(this) 来获取当前 vue 实例。

结束,下班!