computed不能传参?看完你就知道!

22,125 阅读6分钟

最近写的文章可能都是那种长篇大论的干货,看到篇幅就害怕的那种... ...那这篇,我们来点轻松又实用的吧,来讲讲computed的传参、get、set的用法~

老规矩,先看看问题!

  • 小白Vue不知道computed还能有传参写法?!👇
  • 老Vue不理解传参写法的原理,所以每次用computed传参都要百度一下怎么传?!👇
  • 新老Vue都不知道computed居然除了get还有set?!👇

ok!如果你有这样的痛点,那么接着看。本文从源码层面,带你看看computed为什么还能传参!(只要你看懂了,保证你再写computed传参不再需要百度!)再带你看看,computed的set可以怎么玩~😎

tips:如果你还希望进一步了解computed的实现原理,可以戳这里,看文章的彩蛋部分,满满的干货,带你深入computed的源码实现!

一、computed的3种写法

首先看看我们computed的各种写法

  1. 普通函数写法,一个callback返回一个值
  2. 高阶函数写法,一个callback返回一个函数,其实就是变成一个method了。返回的函数可接收参数(这就是可以传参的原理)
  3. 对象写法,写成对象时必须提供get方法,当然也可以设置set方法
<template>
  <div id="app">
    <p>常规computed写法:{{ wholeName1 }}</p>
    <p>computed传参写法:{{ wholeName2('柏然') }}</p>
    <p>computed的get写法:{{ wholeName3 }}</p>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      firstName: '井',
      secondName: '柏然'
    }
  },
  computed: {
    wholeName1 () {
      return  this.firstName + this.secondName
    },
    wholeName2 () {
      return secondName => this.firstName + secondName
    },
    wholeName3: {
      get () {
        return this.firstName + this.secondName
      }
    }
  }
}
</script>
  • 如图,牛逼了!👇 image.png

可见,三种写法都能实现我们的期望输出~不知道平时的你会怎么运用他们解决各种业务疑难呢😜


二、computed传参怎么玩?

1. computed传参的场景

既让讲到传参,我们最先应该想的是,为什么要往computed中传参?传参解决什么问题?

  • 处理字符串的拼接问题
    1. 直接写在模版:{{ msg1 + default1 + default2 + default... }}
    2. 用computed:{{ wholeMsg(msg1) }}
  • 处理数据,比如需要展示百分比,后端只返回了0.xx
    1. 直接写在模板:{{ pointNumber * 100 + '%' }}
    2. 用computed:{{ percent(pointNumber) }}
  • 丧心病狂的超深层的数据?
    1. 直接写在模板:{{ obj.a.b.c.d.e.f.g......name }}
    2. 用computed:{{ getName(obj) }}
  • table某个column要展示某个列表的label,后端只给个key?
  • 一句话总结,写一个function接收一个原始值,返回一个处理后的值,以此处理各种业务场景的需求!

你还别说,对于经常游走在B端的FE们,这些真的只是家常便饭,还有各种奇葩场景就不一一赘述了🤨... ...

经过简单的例子,有没有发现:

  1. 用computed写法可以让模板更整洁
  2. 可读性比各种拼接直接写在模板上更好?
  3. 实现了公共代码抽离,组件各个地方要用到直接一个function接收一个参数就完成了? so👉,灵活运用computed传参的写法,可以解决我们日常开发中的很多问题,让你的开发更便捷~

2.computed传参怎么写?

回顾一下computed的本质实现原理。由此可以知道,其实computed也是用Object.defineProperty做了一个劫持,当我们在template(就是编译后的render函数)中访问到对应的计算属性,就会触发到computed的getter~!然后就会调用我们写computed的那个cb方法~

😜号外:想学习computed具体实现的,记得点进去👆上述链接看彩蛋篇噢~

在案例讲解前computed传参写法,先引入一下高阶函数的概念:

  • 高阶函数是一个函数,它接收函数作为参数将函数作为输出返回
  • 举个例子方便理解~日常开发我们发起异步请求,时常要加个loading、catch错误等,那么我们可能会这样写:
    1. 在每个业务方法中分别处理,如下代码块
    const fetch1 = async () => {
        try {
            this.loading = true
            const result = await postFunction(data)
            ...
        } catch (e) {
            console.error('错误捕获', e)
        } finally{
            this.loading = false
        }
    }
    const fetch2 = async () => {
        try {
            this.loading = true
            const result = await getFunction(params)
            ...
        } catch (e) {
             console.error('错误捕获', e)
        } finally{
            this.loading = false
        }
    }
    
    1. 高阶函数包装,统一处理后返回业务方法
    const loadingAndCatch = cb => {
        return async () => {
            try {
                this.loading = true
                await cb()
            } catch (e) {
                 console.error('错误捕获', e)
            } finally{
                this.loading = false
            }
        }
    }
    const fetch1 = loadingAndCatch(async () => {
        const result = await postFunction(data)
        ...
    })
    const fetch2 = loadingAndCatch(async () => {
        const result = await getFunction(params)
        ...
    })
    

有没有发现,高阶的写法可以统一处理公共逻辑,不再需要每个函数内部实现loading这种公用的东西呢?

接下来,进入你们期待的computed传参!如果你有如下问题👇,保证这篇文章过后你就清晰了

computed: {
    // 不知道把参数写在哪里?是wholeName加参数?还是return的funtion加参数?
    wholeName (???) {
        return function (???) {
            return ...
        }
    }
}

来,我们把文章最开始的案例拿来改造一下,看完你就懂了。这里我们只保留wholeName2这个computed。

  1. Vue源码的computedGetter中打印:'首先,执行computed getter'
  2. 并在wholeName2的cb中打印:'其次,执行computed的cb'
  3. return的function中打印:'最后,模板('柏然')调用并传入参数执行return的function'
// Vue源码
<script>
function createComputedGetter (key) {
  return function computedGetter () {
    console.log('首先执行:computed getter')
    var watcher = this._computedWatchers && this._computedWatchers[key];
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate();
      }
      if (Dep.target) {
        watcher.depend();
      }
      return watcher.value
    }
  }
}
</script>

// App组件代码
<template>
  <div id="app">
    <p>computed传参写法:{{ wholeName2('柏然') }}</p>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      firstName: '井'
    }
  },
  computed: {
    wholeName2 () {
      console.log('其次执行computed的cb:wholeName2的cb')
      return secondName => {
        console.log('最后执行我们模板中调用computed返回的函数!')
        return this.firstName + secondName
      }
    }
  }
}
</script>

直接看结果!👇

image.png

那这下我们可以知道,我们传参是传给返回的函数~而我们返回的函数是被我们的模板所调用的~

就怕你说不清晰!再上个流程图给你,还不清晰你打我~

computed传参.png


三、computed的set可以怎么玩

Vue的单向数据流在某些时候不好处理?

比如:emmm,还是直接给出一个我常用的set的场景吧~

封装组件的时候,由于封装的组件接收外部传入的数据,内部使用v-model会导致外部传入的值在内部被修改。这时候为了维护单向数据流的话,我们不建议直接在组件内部直接改变外部数据的值。这时,我们的computed的set就可以发挥作用了~直接看例子吧👇

<template>
  <!-- 可以看到v-model绑定的是proxyValue,而非直接绑定外部传入的value -->
  <el-select
    v-model="proxyValue" 
  >
    <template v-for="(item, i) in options">
      <el-option
        :key="item.value
        :label="item.label"
        :value="item.value"
      />
    </template>
  </el-select>
</template>

<script>
  export default {
    name: "BaseSelect",
    props: {
      options: {
        type: Array,
        default () {
          return []
        }
      },
      value: {
        type: [String, Array, Number, Boolean],
        default () {
          return []
        }
      }
    },
    computed: {
      proxyValue: {
        get () {
          // 访问的时候直接返回传入的value
          return this.value
        },
        set (val) {
          // 修改了proxyValue,在触发set的同时,通过$emit的方式,让外部去执行值的修改
          this.$emit('update:value', val) 
        }
      }
    }
  }
</script>

补充说明:经掘友评论,这里补充说明一下,避免大家产生误解。通过高阶写法让computed拥有接参能力的同时,computed已经不再是惰性求值的了。也就是说,整个组件只要重新渲染,computed一定会执行,而不是之前的依赖值发生改变才执行。这种方法跟直接写在method里是一样的作用和效果。至于笔者这样使用,是个人的理解。computed的定义就是:计算并return一个计算后的值。如果组件中有需要接收参数并重新计算一个新值,笔者个人更倾向在computed中完成而非直接写在method里。所以,要怎么用完全取决于大佬们~😜这里只是小弟分享的一种用法而已~想学习computed原理的,可以戳这里,看Vue响应式原理

写在最后,回归一下computed是否可以传参这个面试题。其实说computed无法传参是正确的,因为我们正常使用computed时,确实无法直接对我们写的callback传参。但是这种说法也可能是错的,因为本文也介绍了高阶函数的写法,通过黑科技让computed变成method,间接拥有了接收参数的能力,所以也算是能接收参数吧?个人感觉,如果面试的时候回答到这种程度,再从源码层面对computed分析,那这个题肯定是加分题~!

本文也算是我个人一些日常开发中对computed用法的一些小积累吧~ 希望能帮助到你们~

如果看完觉得有所收获的,不妨点个赞吧~爱你们 👍