跳到主要内容

computed

2024年03月18日
柏拉文
越努力,越幸运

一、认识


computed 侦听依赖的响应式数据, 根据传入类型, 生成不同的 Ref 类型的响应式数据。

  • computed(()=>{}): 传入 getter 函数, 返回一个只读的响应式 ref 对象

  • computed({ get(){}, set(){} }): 返回一个可读、可写的响应式 ref 对象

二、语法


computed(()=>{},debuggerOptions?);

computed(
{
get(){

},
set(value){

}
},
debuggerOptions?:
);

三、用法


3.1 可读计算属性

import { ref, type Ref, computed } from 'vue'

const a: Ref<number> = ref(0)

const count = computed(() => {
return a.value * 10
})

3.2 可读可写计算属性

import { ref, type Ref, computed } from 'vue'

const a: Ref<number> = ref(0)

const count = computed({
get() {
return a.value * 10
},
set(value) {
a.value = value
}
})

3.3 收集、管理副作用

import { ref,stop,computed } from 'vue';

let disposables = [];
const count = ref(0);

const doubleCount = computed(()=> count.value * 2);
disposables.push(()=> stop(doubleCount));

setTimeout(()=>{
count.value++;
},2000);

setTimeout(()=>{
disposables.forEach(dispose=> dispose());
disposables = []
},4000);

setTimeout(()=>{
count.value++;
}, 60000);

四、对比


4.1 compute 实现原理

Vue.js 3.0 中, computed 是一个计算属性, 根据传入的 getter 函数, 计算得到一个可读或者可读可写的响应式数据。Computed 具有惰性计算、缓存更新等特点。在 Vue 3 中, Computed 的实现是建立在响应式系统和 ReactiveEffect 之上的一种缓存计算机制, 主要依赖于 ReactiveEffect 来包装计算函数, 并利用调度器和 dirty 标记以及 _value 实现惰性计算与缓存更新。这种设计既保证了 Computed 只在必要时重新计算,又能高效地追踪依赖,实现高性能的响应式数据更新,是 Vue 3 响应式系统的重要组成部分。

一、Computed 底层结构

class ComputedRefImpl {
constructor(getter, setter, isReadonly) {
this.getter = getter;
this.setter = setter;
this._value = undefined;
this.dirty = true;
this.effect = new ReactiveEffect(getter, () => {
if (!this.dirty) {
this.dirty = true;
triggerRefValue(this);
}
});
this.__v_isRef = true;
this[ReactiveFlags.IS_READONLY] = isReadonly;
}

get value() {
trackRefValue(this);
if (this.dirty) {
this._value = this.effect.run();
this.dirty = false;
}
return this._value;
}

set value(newVal) {
if (this.setter) {
this.setter(newVal);
} else {
console.warn('Write operation failed: computed value is readonly.');
}
}
}

二、属性方法:

  • _value: 缓存计算后的结果。

  • dirty: 一个布尔标记,指示当前缓存是否失效。当依赖变化时会被标记为 true,表示下次读取需要重新计算。

  • effect: 内部创建的 ReactiveEffect, 用于运行计算函数,并通过 scheduler 实现依赖变化时标记 computed 为脏。

  • __v_isRef: 标识这是一个 refcomputed 也算作 ref),使得 computed 对象具有统一的访问接口(即通过 .value 访问)。

  • value getter: 当外部访问 computedRef.value 时,首先会检查 dirty 标记。如果为 true,则调用 effect.run() 重新计算,并更新 _value;否则直接返回 _value。

  • value setter(可选):如果用户为 computed 提供了 setter,则 computedRef 实现 setter,调用用户自定义的更新函数;如果没有 setter,则在尝试修改时会给出警告或不允许修改。

三、惰性计算: Computed 内部创建的 ReactiveEffect 会把计算函数包裹进去, 同时传入一个调度器函数(scheduler``)。当计算函数中依赖的响应式数据更新时,scheduler 不会立即调用 effect.run() 重新计算,而是简单地将 computedRefdirty 标记为 true。这样,computed惰性 的, 只有在下次读取 computedRef.value 时才会重新计算。

四、依赖追踪与缓存更新: 1. 缓存, 当 Computedgetter 被执行时, 其返回值被存储在 _value 中。如果在此后依赖没有变化, 则 _value 会直接复用,避免了重复计算。2. 依赖追踪, 当 Computed 内部依赖的响应式数据更新后(例如在 getter 内部通过 reactive 对象的 get 拦截器触发 track``),scheduler 会执行, 将 Computeddirty 标记设为 true,并通过 triggerRefValue 通知所有依赖该 computedeffect(例如在模板中使用该 computed 的组件),使得它们在下一次访问 computed.value 时重新计算。

4.2 computed vs watch

Watch 是基于响应式系统构建的一个高层 API,用于观察响应式数据的变化并在数据变化时触发回调。Watch 具体工作流如下: 1. 响应式依赖追踪, Watch 的本质是创建一个 ReactiveEffect 来观察 数据源(可以是 refreactive 对象、getter 函数或它们的组合)。当数据源中依赖的数据发生变化时, ReactiveEffect 会触发调度器,进而执行 watch 回调。2. 懒执行与调度, Watch 通过传入自定义的调度器(scheduler), 可以控制何时执行回调函数, 默认情况下, watch 会异步调度更新(比如在微任务队列中), 也支持同步(flush: 'sync')或 post-render(flush: 'post')等不同的调度时机。3. 清理与失效, 为了处理异步回调中可能存在的竞争问题,watch 提供了 onInvalidate 回调,允许用户注册清理函数, 如果在异步任务期间依赖发生变化,则会调用该清理函数,以防止过时的回调继续执行。

Computed 是一个计算属性, 根据传入的 getter 函数, 计算得到一个可读或者可读可写的响应式数据。Computed 具有惰性计算、缓存更新等特点。在 Vue 3 中, Computed 的实现是建立在响应式系统和 ReactiveEffect 之上的一种缓存计算机制, 主要依赖于 ReactiveEffect 来包装计算函数, 并利用调度器和 dirty 标记以及 _value 实现惰性计算与缓存更新。这种设计既保证了 Computed 只在必要时重新计算,又能高效地追踪依赖,实现高性能的响应式数据更新,是 Vue 3 响应式系统的重要组成部分。

因此, WatchComputed 依赖收集都是基于 ReactiveEffect 来实现的, 也都传入了调度器进行定制逻辑。但是, ComputedReactiveEffect 包装的是传入的计算函数; Watch 中的 ReactiveEffect 包装的是观察 数据源(可以是 refreactive 对象、getter 函数或它们的组合)的函数。

4.3 computed vs methods

methods: 多次访问 methods 中的方法, 总是多次执行;

computed 是一个计算属性, 根据传入的 getter 函数, 计算得到一个可读或者可读可写的响应式数据。Computed 具有惰性计算、缓存更新等特点。在 Vue 3 中, Computed 的实现是建立在响应式系统和 ReactiveEffect 之上的一种缓存计算机制, 主要依赖于 ReactiveEffect 来包装计算函数, 并利用调度器和 dirty 标记以及 _value 实现惰性计算与缓存更新。这种设计既保证了 Computed 只在必要时重新计算,又能高效地追踪依赖,实现高性能的响应式数据更新,是 Vue 3 响应式系统的重要组成部分。