/** * Intercept mutating methods and emit events */ methodsToPatch.forEach(function (method) { // cache original method var original = arrayProto[method]; def(arrayMethods, method, functionmutator () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ];
var result = original.apply(this, args); var ob = this.__ob__; var inserted; switch (method) { case'push': case'unshift': inserted = args; break case'splice': inserted = args.slice(2); break } if (inserted) { ob.observeArray(inserted); } // notify change ob.dep.notify(); return result }); }); functiondef (obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }); }
Dep.prototype.notify = functionnotify () { // stabilize the subscriber list first var subs = this.subs.slice(); if (!config.async) { // subs aren't sorted in scheduler if not running async // we need to sort them now to make sure they fire in correct // order subs.sort(function (a, b) { return a.id - b.id; }); } for (var i = 0, l = subs.length; i < l; i++) { subs[i].update(); } };
// The current target watcher being evaluated. // This is globally unique because only one watcher // can be evaluated at a time. Dep.target = null; var targetStack = [];
Watcher.prototype.get = functionget () { pushTarget(this); var value; var vm = this.vm; try { value = this.getter.call(vm, vm); } catch (e) { if (this.user) { handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\"")); } else { throw e } } finally { // "touch" every property so they are all tracked as // dependencies for deep watching if (this.deep) { traverse(value); } popTarget(); this.cleanupDeps(); } return value };
看到pushTarget和popTarget是不是很眼熟,这不就是Dep里提供的两个方法嘛 这两个方法的作用就是形成一个闭包 value = this.getter.call(vm, vm) 这一步会触发被劫持数据的get钩子 此时Dep.target就是当前的watcher实例 所有触发getter的对象都会触发dep.depend() 如下:
1 2 3 4 5
depend () { if (Dep.target) { Dep.target.addDep(this) } }
进而触发当前watcher的addDep方法:
1 2 3 4 5 6 7 8 9 10
addDep (dep) { const id = dep.id if (!this.newDepIds.has(id)) { this.newDepIds.add(id) this.newDeps.push(dep) if (!this.depIds.has(id)) { dep.addSub(this) } } }
functionmountComponent ( vm, el, hydrating ) { vm.$el = el; if (!vm.$options.render) { vm.$options.render = createEmptyVNode; { /* istanbul ignore if */ if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') || vm.$options.el || el) { warn( 'You are using the runtime-only build of Vue where the template ' + 'compiler is not available. Either pre-compile the templates into ' + 'render functions, or use the compiler-included build.', vm ); } else { warn( 'Failed to mount component: template or render function not defined.', vm ); } } } callHook(vm, 'beforeMount');
var updateComponent; /* istanbul ignore if */ if (config.performance && mark) { updateComponent = function () { var name = vm._name; var id = vm._uid; var startTag = "vue-perf-start:" + id; var endTag = "vue-perf-end:" + id;
mark(startTag); var vnode = vm._render(); mark(endTag); measure(("vue " + name + " render"), startTag, endTag);
// we set this to vm._watcher inside the watcher's constructor // since the watcher's initial patch may call $forceUpdate (e.g. inside child // component's mounted hook), which relies on vm._watcher being already defined newWatcher(vm, updateComponent, noop, { before: functionbefore () { if (vm._isMounted && !vm._isDestroyed) { callHook(vm, 'beforeUpdate'); } } }, true/* isRenderWatcher */); hydrating = false;
// manually mounted instance, call mounted on self // mounted is called for render-created child components in its inserted hook if (vm.$vnode == null) { vm._isMounted = true; callHook(vm, 'mounted'); } return vm }