🏊
【Vue.js】computedを実装して仕組みを理解する
どうもフロントエンドエンジニアのoreoです。
これまでVue.js Reactivity APIのreactive
とref
に関して解説しました!この記事では、computed
の仕組みについて、実装しながら理解したいと思います。
computed
とは?
1 Composition APIのcomputed
は、リアクティブな値を含むコールバック関数を引数にとり、value
プロパティを持ったオブジェクト型を返します。コールバック関数内のリアクティブな値が変更されると返り値も変更されます。computed
は、下記の様に定義されています。
function computed<T>(
getter: () => T,
debuggerOptions?: DebuggerOptions
): Readonly<Ref<Readonly<T>>>
※今回の記事では、read-only
のcomputed
を扱い、writable
は扱いません。
参考
computed
を実装してみる
2 まず、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.ts
packages/reactivity/src/reactive.ts
packages/reactivity/src/ref.ts
packages/reactivity/src/computed.ts
packages/reactivity/src/effect.ts
4 参考
Discussion