Vue のリアクティブシステム
勉強会で発表した資料です。
Vue のリアクティブシステム、その中でも依存関係のある関数の収集と再実行をどのように実現しているのかについて解説しています。Object.defineProperty, Dep Class and Watcher.
---
# 話すこと
- Vue のリアクティブシステムの一部を説明
- リアクティブシステム
=> モデルの変更がDOMに反映される仕組み --- ![inline](model-dom.png) https://blog.thoughtram.io//angular/2016/02/22/angular-2-change-detection-explained.html --- ![inline](data.png) [リアクティブの探求 — Vue.js](https://jp.vuejs.org/v2/guide/reactivity.html) --- # 一旦サンプルを --- **値段**と**数**から**合計金額**を表示するアプリケーション
=> モデルの変更がDOMに反映される仕組み --- ![inline](model-dom.png) https://blog.thoughtram.io//angular/2016/02/22/angular-2-change-detection-explained.html --- ![inline](data.png) [リアクティブの探求 — Vue.js](https://jp.vuejs.org/v2/guide/reactivity.html) --- # 一旦サンプルを --- **値段**と**数**から**合計金額**を表示するアプリケーション
<!-- html --> <div id="app"> <div>price: {{ price }}</div> <div>quantity: {{ quantity }}</div> <div>total: {{ total }}</div> </div>
// js const app = new Vue({ el: '#app', data: { price: 100, quantity: 2 }, computed: { total() { return this.price * this.quantity; } } });https://stackblitz.com/edit/vue-reactivity(https://stackblitz.com/edit/vue-reactivity) --- - `data` - Vue インスタンスのためのデータオブジェクト - Vue インスタンスが作成されるとリアクティブシステムに追加される - `computed` - Vue インスタンスに組み込まれる算出プロパティ - 算出プロパティは依存関係にもとづきキャッシュされる --- # 今日のゴールは ---
let data = { price: 100, quantity: 2 }; let total = data.price * data.quantity; console.log(total); // => 200 data.quantity = 3; console.log(total); // => 300 ?--- # その1 ## 関数を保存 ---
let data = { price: 100, quantity: 2 }; // 再計算できるように関数を保存 let target = () => data.total = data.price * data.quantity; target(); console.log(data.total); // => 200 data.quantity = 3; target(); console.log(data.total); // => 300 // https://stackblitz.com/edit/vue-reactivity-step-by-step?file=step-96.js--- - 保存される関数は複数必要 - 依存関係のある関数だけを管理したい --- # その2 ## Dependency Class ---
export let target = null; export class Dep { constructor() { this.subscribers= []; } depend() { if (target && !this.subscribers.includes(target)) { this.subscribers.push(target); } } notify() { this.subscribers.forEach(sub => sub()); } }---
let data = { price: 100, quantity: 2 }; let watcher = () => data.total = data.price * data.quantity; const dep = new Dep(); target = watcher; dep.depend(); target(); target = null; console.log(data.total); // => 200 data.quantity = 3; dep.notify(); console.log(data.total); // => 300 // https://stackblitz.com/edit/vue-reactivity-step-by-step?file=step-97.js--- # Dependency Class の役割 --- ![inline](dep.png) https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d(https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d) --- ![inline](dep-2.png) https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d(https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d) --- ![inline](dep-3.png) https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d(https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d) --- - プロパティごとに `Dep` インスタンスが必要 - 依存関係のある関数だけを管理したい - (未解決) --- # その3 ## `Object.defineProperty` ---
Object.keys(data).forEach(key => { let _value = data[key]; Object.defineProperty(data, key, { get() { console.log('get', key, _value); return _value; }, set(value) { console.log('set', key, _value); _value = value; } }); });---
Object.keys(data).forEach(key => { let _value = data[key]; const dep = new Dep(); Object.defineProperty(data, key, { get() { dep.depend(); return _value; }, set(value) { _value = value; dep.notify(); } }); }); // ... target = watcher; target(); target = null;--- # 完成
console.log(data.total); // => 200 data.quantity = 3; console.log(data.total); // => 300https://stackblitz.com/edit/vue-reactivity-step-by-step?file=step-99.js(https://stackblitz.com/edit/vue-reactivity-step-by-step?file=step-99.js) --- ![inline](data_with_annotations.png) https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d(https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d) --- # 振り返り - Vue のリアクティブシステムの一部を説明 - `Object.defineProperty` - `Dep` class - `Watcher` --- # 参考資料 - [The Best Explanation of JavaScript Reactivity 🎆 – Vue Mastery – Medium](https://medium.com/vue-mastery/the-best-explanation-of-javascript-reactivity-fea6112dd80d) --- # ご静聴ありがとうございました --- # おまけ1 ## Angular の場合 ---
export class AppComponent { price: number; quantity: number; constructor() { this.price = 100; this.quantity = 2; (window as any).app = this; } get total() { return this.price * this.quantity; } } // コンソールから実行 window.app.quantity = 10; // => DOMは更新されない // => ボタンクリックなどをすると反映されるhttps://stackblitz.com/edit/vue-reactivity-case-of-angular(https://stackblitz.com/edit/vue-reactivity-case-of-angular) --- Angular のレンダリングに興味がある方は [日本語訳 Angular 2 Change Detection Explained |