Vueのリアクティブ要素にはrefとreactiveがあります。
たとえばrefをつかったリアクティブの場合、
const count = ref(0);
watchEffect(() => console.log(count.value)); // -> 0
count.value = 1; // -> 1
countが変更されるたびにwatchEffect()に登録した関数が実行されます。
countが変更されたらconsole.log()が呼び出される仕組みがわからないので理解していきます。
const count = ref(0)
countにはref(0)が代入されています。このrefはproxyです。
proxyは値とHandlerを持っています。ref(0)だと0が値です。ではHandlerは?
Handlerはrefの中に隠蔽されています。下のコードのような感じです。
Handlerはgetterとsetterを持っています。
getterはtrack()を、setterはtrigger()を持っています。
このtrack()とtrigger()は何をしているのでしょうか?
const dinner = { meal: 'tacos' }
const handler = {
get(target, prop, receiver) {
track(target, prop)
return Reflect.get(...arguments)
},
set(target, key, value, receiver) {
trigger(target, key)
return Reflect.set(...arguments)
}
}
const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)
track()
trackは
Map<proxyのオブジェクト, proxyを呼び出す関数群>
を保存します。
つまり最初のコードでは、
map.set(count, new Set( {() => console.log(count.value)} ))
を行っています。
const count = ref(0);
watchEffect(() => console.log(count.value));
trigger()
triggerはtrack()で保存したproxyに関連する関数をすべて呼び出します。
const functionSet = map.get(count) // -> Set<Function>
functionSet.forEach( { // 関数を実行 } )
ふりかえると
watchEffectの中でcountを呼び出すと、呼び出したラムダが登録されます。
count.valueが変更されると登録されたラムダが実行されます。
const count = ref(0);
watchEffect(() => console.log(count.value)); // -> 0
count.value = 1; // -> 1
リアクティブがどんな感じで実現されているのか雰囲気がつかめてきました。
参考