前言
computed又称为计算属性,在vue项目中你会经常用到, 那么在vue3中的computed又是如何实现的呢?当计算属性不发生变化, 就不会再次去调用函数, 这又是如何实现的呢?下面来进行我们对
computed的简单实现吧!
实现
首先我们知道vue3中computed是一个函数, 并且接收一个参数, 暂且叫这个参数getterOrOptions,这个参数可以是一个函数(getter), 也可以是一个对象(包含getter和setter), 调用computed之后会返回一个ComputedRefImpl实例
// computed.ts
/**
* cpmputed函数
* @param getterOrOptions getter函数或者 options配置
* @returns ComputedRefImpl实例
*/
export const computed = (getterOrOptions) => {
let getter;
let setter;
// 如果是一个函数
const onlyGetter = isFunction(getterOrOptions)
if(onlyGetter) {
getter = getterOrOptions
// setter就会警告⚠️
setter = () => {
console.warn('Write operation failed: computed value is readonly')
}
}else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
const cRef = new ComputedRefImpl(getter, setter)
return cRef
}
接下来主要去实现ComputedRefImpl就行了
// ComputedRefImpl实例
class ComputedRefImpl {
public _dirty = true // 默认为true (作为是否缓存的标识)
public _value
public effect
constructor( getter, public setter) {
// effect可以收集依赖和触发依赖,
this.effect = effect(getter, {
lazy: true,
scheduler: () => {
if(!this._dirty) {
this._dirty = true
trigger(this, TriggerOrTypes.SET, 'value') // 触发依赖去更新
}
}
})
}
get value() {
// 如果是脏的, 我们采取执行effect, 也就是执行用户传过来的函数,并且将返回值给到this._value
if(this._dirty) {
this._value = this.effect()
this._dirty = false
}
// 这里收集.value的依赖, 主要是为了实现, computed所依赖的属性变了, effect会重新触发依赖和依赖更新
track(this, trackOpTypes.GET, 'value') // 收集使用.value的依赖
return this._value
}
set value(newVal) {
this.settet(newVal)
}
}
实现ComputedRefImpl需要注意的几个点:
- 1.通过
_dirty来作为是否缓存的标识 - 2.主要用过
effect来实现内部的逻辑, 详见effect
测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="app"></div>
<script src="../node_modules/@vue/reactivity/dist/reactivity.global.js"></script>
<script>
let { ref, effect, computed } = VueReactivity
// 测试基本功能(单一get)
// const count = ref(1)
// const plusOne = computed(() => count.value + 1)
// console.log(plusOne.value) // 2
// 测试基本功能2(get 和 set 的使用)
// const count = ref(1)
// const plusOne = computed({
// get: () => count.value + 1,
// set: (val) => {
// count.value = val - 1
// }
// })
// // debugger
// plusOne.value = 1
// console.log(count.value) // 0
// 测试基本功能3(依赖的属性发生变化, 对应计算属性也发生变化)
const count = ref(1)
const plusOne = computed(() => count.value + 1)
effect(() => {
console.log(plusOne.value ); // 依赖收集了.value
})
debugger
count.value = 10
</script>
</body>
</html>
可以通过以上案例去测试我们写的computed, 如果你有运行我们的案例, 可以看到computed的基本功能都已经实现了