什么是computed?
他是组件的计算属性,依赖其他状态的变化而生成的状态。
如何使用computed
源码说明:要么传递一个函数作为getter,要么传递一个包含get和set 的对象
<script setup>
import { ref,computed} from 'vue'const count = ref(1);
const comCount = computed(()=>count.value+'computed')
</script><template><h1>{{ count }}</h1><div>{{comCount}}</div>
</template>
<script setup>
import { ref,computed} from 'vue'const count = ref('1');
const comCount = computed({get:()=>count.value+'--get',set:(val)=>count.value=val+'--set'
})
// 输出:1 1--get
comCount.value='2';
// 输出:2--set 2--set--get
</script><template><h1>{{ count }}</h1><div>{{comCount}}</div>
</template>
computed缓存值的原理
大家请先看reactive响应式相关代码,要不在看这边文章时,有点蒙。
我们都知道,computed会缓存数据,只有依赖数据发生变化时才会重新计算,否则读取以前的值。
分析源码:
// ComputedGetter就是一个函数类型
export function computed<T>(getter: ComputedGetter<T>,debugOptions?: DebuggerOptions
): ComputedRef<T>// WritableComputedOptions内部定义get和set类型
export function computed<T>(options: WritableComputedOptions<T>,debugOptions?: DebuggerOptions
): WritableComputedRef<T>// 主要逻辑实现
export function computed<T>(getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>,debugOptions?: DebuggerOptions,isSSR = false
) {let getter: ComputedGetter<T>let setter: ComputedSetter<T>const onlyGetter = isFunction(getterOrOptions)// 如果是函数直接赋值getter,否则将对象内的get和set方法分别赋值getter和setterif (onlyGetter) {getter = getterOrOptionssetter = __DEV__? () => {console.warn('Write operation failed: computed value is readonly')}: NOOP} else {getter = getterOrOptions.getsetter = getterOrOptions.set}const cRef = new ComputedRefImpl(getter, setter, onlyGetter || !setter, isSSR)if (__DEV__ && debugOptions && !isSSR) {cRef.effect.onTrack = debugOptions.onTrackcRef.effect.onTrigger = debugOptions.onTrigger}return cRef as any
}
上述代码只是定义getter和setter,并进行赋值函数。然后创建ComputedRefImpl实例,并返回此实例。那么我们可以知道缓存功能就在这个函数中。
export class ComputedRefImpl<T> {public dep?: Dep = undefinedprivate _value!: Tpublic readonly effect: ReactiveEffect<T>public readonly __v_isRef = truepublic readonly [ReactiveFlags.IS_READONLY]: boolean = falsepublic _dirty = truepublic _cacheable: booleanconstructor(getter: ComputedGetter<T>,private readonly _setter: ComputedSetter<T>,isReadonly: boolean,isSSR: boolean) {this.effect = new ReactiveEffect(getter, () => {if (!this._dirty) {this._dirty = truetriggerRefValue(this)}})this.effect.computed = thisthis.effect.active = this._cacheable = !isSSRthis[ReactiveFlags.IS_READONLY] = isReadonly}get value() {// the computed ref may get wrapped by other proxies e.g. readonly() #3376const self = toRaw(this)trackRefValue(self)if (self._dirty || !self._cacheable) {self._dirty = falseself._value = self.effect.run()!}return self._value}set value(newValue: T) {this._setter(newValue)}
}
上述代码:调用此构造函数时,会创建ReactiveEffect构造函数(创建响应式副作用函数),并将实例赋值effect。将ComputedRefImpl实例赋值副作用函数实例的computed函数,在依赖更新时的判断条件。赋值副作用函数为活跃状态active。内部定义了get和set 方法,当使用实例调用或赋值时会触发get和set。 初始化不会执行getter,当调用时才会触发,这是因为调用时执行了ReactiveEffect实例的run方法,才会执行getter方法,并将返回值赋值_value。
ReactiveEffect 在讲响应式中有讲到过,大家可以查看。
这里在读取computed时,也会进行依赖收集(上边所说的effect)。初始化_dirty=true,满足条件讲_dirty赋值false,所以在此读取computed不会重新计算,然后执行run方法得到getter函数返回值赋值_value,并返回_value。
依赖更新时如何监听到的呢? 上述说了读取时会进行依赖收集,在创建ReactiveEffect实例时,存储了scheduler,再进行依赖更新(trigger())会判断是否有scheduler,有则执行函数。就会再次进行依赖更新就会重新触发getter函数,并将_dirty赋值true。这样在依赖变更时,读取computed值就会变化。