- 7.15 watch
- 7.15.1 依赖收集
- 7.15.2 派发更新
7.15 watch
到这里,关于响应式系统的分析大部分内容已经分析完毕,我们上一节还遗留着一个问题,Vue对用户手动添加的watch如何进行数据拦截。我们先看看两种基本的使用形式。
// watch选项var vm = new Vue({el: '#app',data() {return {num: 12}},watch: {num() {}}})vm.num = 111// $watch api方式vm.$watch('num', function() {}, {deep: ,immediate: ,})
7.15.1 依赖收集
我们以watch选项的方式来分析watch的细节,同样从初始化说起,初始化数据会执行initWatch,initWatch的核心是createWatcher。
function initWatch (vm, watch) {for (var key in watch) {var handler = watch[key];// handler可以是数组的形式,执行多个回调if (Array.isArray(handler)) {for (var i = 0; i < handler.length; i++) {createWatcher(vm, key, handler[i]);}} else {createWatcher(vm, key, handler);}}}function createWatcher (vm,expOrFn,handler,options) {// 针对watch是对象的形式,此时回调回选项中的handlerif (isPlainObject(handler)) {options = handler;handler = handler.handler;}if (typeof handler === 'string') {handler = vm[handler];}return vm.$watch(expOrFn, handler, options)}
无论是选项的形式,还是api的形式,最终都会调用实例的$watch方法,其中expOrFn是监听的字符串,handler是监听的回调函数,options是相关配置。我们重点看看$watch的实现。
Vue.prototype.$watch = function (expOrFn,cb,options) {var vm = this;if (isPlainObject(cb)) {return createWatcher(vm, expOrFn, cb, options)}options = options || {};options.user = true;var watcher = new Watcher(vm, expOrFn, cb, options);// 当watch有immediate选项时,立即执行cb方法,即不需要等待属性变化,立刻执行回调。if (options.immediate) {try {cb.call(vm, watcher.value);} catch (error) {handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));}}return function unwatchFn () {watcher.teardown();}};}
$watch的核心是创建一个user watcher,options.user是当前用户定义watcher的标志。如果有immediate属性,则立即执行回调函数。而实例化watcher时会执行一次getter求值,这时,user watcher会作为依赖被数据所收集。这个过程可以参考data的分析。
var Watcher = function Watcher() {···this.value = this.lazy? undefined: this.get();}Watcher.prototype.get = function get() {···try {// getter回调函数,触发依赖收集value = this.getter.call(vm, vm);}}
7.15.2 派发更新
watch派发更新的过程很好理解,数据发生改变时,setter拦截对依赖进行更新,而此前user watcher已经被当成依赖收集了。这个时候依赖的更新就是回调函数的执行。
