最近写的文章可能都是那种长篇大论的干货,看到篇幅就害怕的那种… …那这篇,我们来点轻松又实用的吧,来讲讲computed的传参用法~
老规矩,先看看问题!
- 小白Vue不知道computed有传参写法?!?
- 老Vue不理解传参的原理,所以每次用computed传参都要百度一下怎么传?!?
- 新老Vue都不知道computed居然除了
get
还有set
?!?
ok!如果你有这样的痛点,那么接着看。本文从源码层面,带你看看computed为什么还能传参!(只要你看懂了,保证你再写computed传参不再需要百度!)再带你看看,computed的set可以怎么玩~?
tips:如果你还希望进一步了解computed的实现原理,可以戳这里,看文章的彩蛋部分,满满的干货,带你深入computed的源码实现!
一、computed的3种写法
首先看看我们computed的各种写法
- 普通函数写法,一个callback返回一个值
- 高阶函数写法,一个callback返回一个函数,返回的函数可接收参数(这就是可以传参的原理)
- 对象写法,写成对象时必须提供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>
复制代码
- 如图,牛逼了!?
可见,三种写法都能实现我们的期望输出~不知道平时的你会怎么运用他们解决各种业务疑难呢?
二、computed传参怎么玩?
1. computed传参的场景
既让讲到传参,我们最先应该想的是,为什么要往computed中传参?传参解决什么问题?
- 处理字符串的拼接问题
- 直接写在模版:
{{ msg1 + default1 + default2 + default... }}
- 用computed:
{{ wholeMsg(msg1) }}
- 直接写在模版:
- 处理数据,比如需要展示百分比,后端只返回了
0.xx
- 直接写在模板:
{{ pointNumber * 100 + '%' }}
- 用computed:
{{ percent(pointNumber) }}
- 直接写在模板:
- 丧心病狂的超深层的数据?
- 直接写在模板:
{{ obj.a.b.c.d.e.f.g......name }}
- 用computed:
{{ getName(obj) }}
- 直接写在模板:
- table某个column要展示某个列表的label,后端只给个key?
- 一句话总结,写一个
function
,接收一个原始值,返回一个处理后的值,以此处理各种业务场景的需求!
你还别说,对于经常游走在B端的FE们,这些真的只是家常便饭,还有各种奇葩场景就不一一赘述了?… …
经过简单的例子,有没有发现:
- 用computed写法可以让模板更整洁?
- 可读性比各种拼接直接写在模板上更好?
- 实现了公共代码抽离,组件各个地方要用到直接一个function接收一个参数就完成了?
so?,灵活运用computed传参的写法,可以解决我们日常开发中的很多问题,让你的开发更便捷~
2.computed传参怎么写?
回顾一下computed的本质实现原理。由此可以知道,其实computed也是用Object.defineProperty做了一个劫持,当我们在template(就是编译后的render函数)中访问到对应的计算属性,就会触发到computed的getter~!然后就会调用我们写computed的那个cb方法~
?号外:想学习computed具体实现的,记得点进去?上述链接看彩蛋篇噢~
在案例讲解前computed传参写法,先引入一下高阶函数的概念:
- 高阶函数是一个函数,它接收函数作为参数或将函数作为输出返回
- 举个例子方便理解~日常开发我们发起异步请求,时常要加个loading、catch错误等,那么我们可能会这样写:
- 在每个业务方法中分别处理,如下代码块
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 } } 复制代码
- 用高阶函数包装,统一处理后返回业务方法
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。
- 在Vue源码的computedGetter中打印:
'首先,执行computed getter'
- 并在wholeName2的cb中打印:
'其次,执行computed的cb'
- 在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>
复制代码
直接看结果!?
那这下我们可以知道,我们传参是传给返回的函数~而我们返回的函数是被我们的模板所调用的~
就怕你说不清晰!再上个流程图给你,还不清晰你打我~
三、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时,确实无法直接对我们写的callback传参。但是这种说法也可能是错的,因为本文也介绍了高阶函数的写法,通过黑科技让computed间接拥有了接收参数的能力,所以也算是能接受参数吧?个人感觉,如果面试的时候回答到这种程度,再结从源码层面分析,那这个题肯定是加分题~!
本文也算是我个人一些日常开发中对computed
用法的一些小积累吧~ 希望能帮助到你们~
如果看完觉得有所收获的,不妨点个赞吧~爱你们 ?