Vue 2.5.17
// 4069
vm._self = vm;
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm); // resolve injections before data/props
initState(vm);
initProvide(vm); // resolve provide after data/props
callHook(vm, 'created');
// 3303
function initState (vm) {
vm._watchers = [];
var opts = vm.$options;
if (opts.props) { initProps(vm, opts.props); }
if (opts.methods) { initMethods(vm, opts.methods); }
if (opts.data) {
initData(vm);
} else {
observe(vm._data = {}, true /* asRootData */);
}
if (opts.computed) { initComputed(vm, opts.computed); }
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch);
}
}
// 3422
var computedWatcherOptions = { lazy: true };
function initComputed(vm, computed) {
var watchers = (vm._computedWatchers = Object.create(null))
var isSSR = isServerRendering()
for (var key in computed) {
var userDef = computed[key]
var getter = typeof userDef === 'function' ? userDef : userDef.get
if ('development' !== 'production' && getter == null) {
warn('Getter is missing for computed property "' + key + '".', vm)
}
if (!isSSR) {
watchers[key] = new Watcher(vm, getter || noop, noop, computedWatcherOptions)
}
if (!(key in vm)) {
defineComputed(vm, key, userDef)
} else {
if (key in vm.$data) {
warn('The computed property "' + key + '" is already defined in data.', vm)
} else if (vm.$options.props && key in vm.$options.props) {
warn('The computed property "' + key + '" is already defined as a prop.', vm)
}
}
}
}
Object.defineProperty
初始化。非 SSR 下会调用 createComputedGetter
方法对原始的 getter 进行加工// 3286
var sharedPropertyDefinition = {
enumerable: true,
configurable: true,
get: noop,
set: noop
};
// 3465
function defineComputed(target, key, userDef) {
var shouldCache = !isServerRendering()
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache ? createComputedGetter(key) : userDef
sharedPropertyDefinition.set = noop
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: userDef.get
: noop
sharedPropertyDefinition.set = userDef.set ? userDef.set : noop
}
if ('development' !== 'production' && sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function() {
warn('Computed property "' + key + '" was assigned to but it has no setter.', this)
}
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
createComputedGetter
是如何加工的,在获取计算属性的值时候其实是拿的 watcher.value。在这里我们先不管 evaluate 和 depend 是干什么的我们先看一下 watcher 如何定义// 3489
function createComputedGetter (key) {
return function computedGetter () {
var watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
if (watcher.dirty) {
watcher.evaluate();
}
if (Dep.target) {
watcher.depend();
}
return watcher.value
}
}
}
{ lazy: true}
,这时候我们在定义 watcher 的时候不会触发 get 方法,而反过来看步骤 4,当 dirty = true
的时候首先会调用 watcher.evaluate()
// 710
Dep.target = null;
var targetStack = [];
function pushTarget (_target) {
if (Dep.target) { targetStack.push(Dep.target); }
Dep.target = _target;
}
function popTarget () {
Dep.target = targetStack.pop();
}
// 3082
var Watcher = function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
// ...
this.dirty = this.lazy;
// ...
this.value = this.lazy ? undefined : this.get()
}
// 3235
Watcher.prototype.get = function get() {
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
}
// 3246
Watcher.prototype.evaluate = function evaluate () {
this.value = this.get();
this.dirty = false;
};
defineReactive
方法// 966
function defineReactive(obj, key, val, customSetter, shallow) {
// ...
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
var value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
set: function reactiveSetter(newVal) {
var value = getter ? getter.call(obj) : val
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if ('development' !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
dep.depend()
// 693
Dep.prototype.depend = function depend() {
if (Dep.target) {
Dep.target.addDep(this)
}
}
// 3169
Watcher.prototype.addDep = function addDep(dep) {
var id = dep.id
if (!this.newDepIds.has(id)) {
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
dep.addSub(this)
}
}
}
// 684
Dep.prototype.addSub = function addSub(sub) {
this.subs.push(sub)
}
dep.notify()
方法最终通知改变 dirty = true,从而重新计算 valuedep.notify()
// 699
Dep.prototype.notify = function notify() {
// stabilize the subscriber list first
var subs = this.subs.slice()
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
// 3200
Watcher.prototype.update = function update() {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
cleanupDeps
方法,步骤 5。将 newDeps 中的依赖转存到 deps 中// 3176
Watcher.prototype.cleanupDeps = function cleanupDeps () {
var this$1 = this;
var i = this.deps.length;
while (i--) {
var dep = this$1.deps[i];
if (!this$1.newDepIds.has(dep.id)) {
dep.removeSub(this$1);
}
}
var tmp = this.depIds;
this.depIds = this.newDepIds;
this.newDepIds = tmp;
this.newDepIds.clear();
tmp = this.deps;
this.deps = this.newDeps;
this.newDeps = tmp;
this.newDeps.length = 0;
};
// 689
Dep.prototype.removeSub = function removeSub (sub) {
remove(this.subs, sub);
};
watcher.depend()
(步骤 4)。其中的 this$1.deps 就是 computedA 的依赖,这个方法将 computedB 的 watcher 添加到 computedA 的依赖中// 3254
Watcher.prototype.depend = function depend () {
var this$1 = this;
var i = this.deps.length;
while (i--) {
this$1.deps[i].depend();
}
};
// 2708
if (vm._watcher) {
vm._watcher.teardown();
}
var i = vm._watchers.length;
while (i--) {
vm._watchers[i].teardown();
}
// 3266
Watcher.prototype.teardown = function teardown () {
var this$1 = this;
if (this.active) {
// remove self from vm's watcher list
// this is a somewhat expensive operation so we skip it
// if the vm is being destroyed.
if (!this.vm._isBeingDestroyed) {
remove(this.vm._watchers, this);
}
var i = this.deps.length;
while (i--) {
this$1.deps[i].removeSub(this$1);
}
this.active = false;
}
};