Vue 經過 watch 選項提供了一個更通用的方法,來響應數據的變化。當須要在數據變化時執行異步或開銷較大的操做時,這個方式是最有用的。vue
一個對象,鍵是須要觀察的表達式,值是對應回調函數。值也能夠是方法名,或者包含選項的對象。Vue 實例將會在實例化時調用 $watch(),遍歷 watch 對象的每個 property。文檔傳送git
示例:github
// 定義watchlet vm = new Vue({ el: '#app', data() {return { name: 'vue2'} }, watch: {name(newVal, oldVal) { console.log(`watch name change form ${oldVal} to ${newVal}`); }, } })// 改變namesetTimeout(() => { vm.name = 'vue3'}, 1000)// 控制檯打印// watch name change form vue2 to vue3複製代碼
核心原理就是對傳入Vue options的watch對象裏面的全部的屬性,都添加一個自定義watcher,收集對應屬性的依賴,而後當這個依賴項更新的時候,不只通知它的渲染watcher(可能沒有),也會通知當前的這個自定義watcher,從而叫到你定義的callback,完成函數內的邏輯。api
首先,watcher綁定的時候,默認要執行一遍this.get方法,這樣咱們就完成了渲染watcher同樣的行爲,那就是'touch'到這個屬性,讓依賴項感知到了本身被依賴,添加訂閱者,而且此時的值就是初始值,咱們把它記錄到watcher上,this.value = this.get();。而後呢,每一次watcher更新調用update方法,咱們都會從新調用this.get,那麼此時的返回值是否是就是最新值了呢?再加上咱們先前記錄的this.value,很棒,咱們有了newValue和oldValue了!緩存
// 添加$watch mixinexport function stateMixin(Vue) { Vue.prototype.$watch = function (key, handler, options = {}) {const vm = this; options.user = true;new Watcher(vm, key, handler, options); }; }// 對傳入的watch添加自定義watcherfunction initWatch() { const vm = this; let watchs = vm.$options.watch; for (let key in watchs) {let handler = watchs[key];if (Array.isArray(handler)) { for (let i = 0; i < handler.length; i++) { createWatcher(vm, key, handler[i]); } } else { createWatcher(vm, key, handler); } } }function createWatcher(vm, key, handler) { vm.$watch(key, handler); }複製代碼b. 修改Watcher類,應對自定義watcher屬性的狀況
// observer/watcher.js constructor(vm, exprOrFn, cb, options) { this.id = "watcher-" + id++; this.vm = vm; this.exprOrFn = exprOrFn; this.cb = cb; this.deps = []; this.depsId = new Set(); this.options = options; this.user = !!options.user; if (typeof exprOrFn === "string") {this.getter = function () { let path = exprOrFn.split("."); let obj = path.reduce((pre, currentPath) => {return pre[currentPath]; }, vm); return obj; }; } else {this.getter = exprOrFn; } this.value = this.get(); } get() { pushTarget(this); // 記錄老的值 const value = this.getter.call(this.vm); popTarget(this); // 返回老的值 return value; } update() { // vue中的更新操做是異步的 // 屢次調用update 我但願先將watcher緩存下來,等一會一塊兒更新 queueWatcher(this); }run() { const oldValue = this.value; const newValue = this.get(); if (this.user) {this.cb(newValue, oldValue);// 別忘了把新的值記錄爲下次的老值this.value = newValue; } }複製代碼
github.com/Sotyoyo/do-… 分支do-watchapp
若是你在非掘金地址看到這篇文章,這裏有原文傳送門,您的點贊是對我最大的支持!完異步