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
若是你在非掘金地址看到这篇文章,这里有原文传送门,您的点赞是对我最大的支持!完异步