🏊
【Vue.js】computedを実装して仕組みを理解する
どうもフロントエンドエンジニアのoreoです。
これまでVue.js Reactivity APIのreactiveとrefに関して解説しました!この記事では、computedの仕組みについて、実装しながら理解したいと思います。
1 Composition APIのcomputedとは?
computedは、リアクティブな値を含むコールバック関数を引数にとり、valueプロパティを持ったオブジェクト型を返します。コールバック関数内のリアクティブな値が変更されると返り値も変更されます。computedは、下記の様に定義されています。
function computed<T>(
getter: () => T,
debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>
※今回の記事では、read-onlyのcomputedを扱い、writableは扱いません。
参考
2 computedを実装してみる
まず、refを作成する時と同じように、targetMap、track、triggerなどを作成します。
const targetMap = new WeakMap();
let activeEffect = null;
function track(target, key) {
if (activeEffect) {
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(activeEffect);
}
}
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) {
return;
}
let dep = depsMap.get(key);
if (dep) {
dep.forEach((eff) => {
eff();
});
}
}
function ref(raw) {
const r = {
get value() {
track(r, "value");
return raw;
},
set value(newVal) {
raw = newVal;
trigger(r, "value");
},
};
return r;
}
function effect(eff) {
activeEffect = eff;
activeEffect();
activeEffect = null;
}
各用語の解説とその役割は👇になります。実装内容の詳細は、reactiveの記事をご確認ください。
| 用語 | 役割 |
|---|---|
| activeEffect | 値の変更を検知して実行する処理を格納する変数 |
| effect | activeEffectの実行やresetを行う関数 |
| dep | activeEffectを格納するSetオブジェクト |
| depsMap | プロパティとdepを紐づけるMapオブジェクト |
| targetMap | オブジェクトとdepsMap紐づけるWeakMapオブジェクト |
| track | depにactiveEffectを格納する関数 |
| trigger | depに格納されているactiveEffectを実行する関数 |
次にcomputedを定義します。effect関数の中で、コールバック関数の返り値をrefオブジェクトであるresultに代入することで、targetMapにコールバック関数を登録し、それを返します。
function computed(getter) {
//refオブジェクトの作成
let result = ref();
//コールバック関数(getter)の返り値をresult.valueに代入
effect(() => (result.value = getter()));
return result;
}
let price = 100;
let discountLate = ref(0.9);
let salePrice = computed(() => {
return price * discountLate.value;
});
console.log(salePrice.value); //90が出力
この時のtargetMapのイメージは👇になります。

この状態で、refオブジェクトであるdiscountLateを更新すると、triggerが実行され、salePriceも更新される様になります!これでcomputedの完成です!
discountLate.value = 0.7;
console.log(salePrice.value); //70が出力!!!
3 最後に
3回の記事に分けて、reactive、ref、computedを実装しました。内部実装を知っておくといろんな場面で応用が効きそうですね。Vue3のソースコードで、今回実装したような処理が記載されているのは下記になりますので、興味のある型は覗いてみてください!
packages/reactivity/src/baseHandlers.tspackages/reactivity/src/reactive.tspackages/reactivity/src/ref.tspackages/reactivity/src/computed.tspackages/reactivity/src/effect.ts
4 参考
Discussion