先來個流程圖,水平有限,湊活看吧-_-|| 首先在創建一個Vue應用時: Vue構造函數源碼: 在initState方法中會初始化data、watch和computed,並調用observe函數監聽data(Object.defineProperty): 1、observe observe在init ...
先來個流程圖,水平有限,湊活看吧-_-||
首先在創建一個Vue應用時:
var app = new Vue({ el: '#app', data: { message: 'Hello Vue!' } })
Vue構造函數源碼:
//創建Vue構造函數 function Vue (options) { if (!(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword'); } this._init(options); } //_init方法,會初始化data,watch,computed等 Vue.prototype._init = function (options) { var vm = this; // a uid vm._uid = uid$3++; ...... // expose real self vm._self = vm; initLifecycle(vm); initEvents(vm); initRender(vm); callHook(vm, 'beforeCreate'); initInjections(vm); // resolve injections before data/props initState(vm); ...... };
在initState方法中會初始化data、watch和computed,並調用observe函數監聽data(Object.defineProperty):
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);//initData中也會調用observe方法 } else { observe(vm._data = {}, true /* asRootData */); } if (opts.computed) { initComputed(vm, opts.computed); } if (opts.watch && opts.watch !== nativeWatch) { initWatch(vm, opts.watch); } }
1、observe
observe在initState 時被調用,為vue實例的data屬性值創建getter、setter函數,在setter中dep.depend會把watcher實例添加到Dep實例的subs屬性中,在getter中會調用dep.notify,調用watcher的update方法。
/**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
* 該函數在initState中有調用
*/
function observe (value, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
var ob;
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__;
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value);
}
if (asRootData && ob) {
ob.vmCount++;
}
re * Observer class that is attached to each observed
* object. Once attached, the observer converts the target
* object's property keys into getter/setters that
* collect dependencies and dispatch updates.
*/
var Observer = function Observer (value) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods);
} else {
copyAugment(value, arrayMethods, arrayKeys);
}
this.observeArray(value);
} else {
this.walk(value);
}
};
/**
* Walk through all properties and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]);
}
};
/**
* Define a reactive property on an Object.
*/
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
var getter = property && property.get;
var setter = property && property.set;
if ((!getter || setter) && arguments.length === 2) {
val = obj[key];
}
var childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
//Dep.target 全局變數指向的就是指向當前正在解析生成的 Watcher
//會執行到dep.addSub,將Watcher添加到Dep對象的Watcher數組中
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 (customSetter) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();//如果數據被重新賦值了, 調用 Dep 的 notify 方法, 通知所有的 Watcher
} }); }
2、Dep
Watcher的update方法是在new Dep的notify的方法中被調用的
/**
* A dep is an observable that can have multiple
* directives subscribing to it.
*/
var Dep = function Dep () {
this.id = uid++;
this.subs = [];
};
//設置某個Watcher的依賴
//這裡添加Dep.target,用來判斷是不是Watcher的構造函數調用
//也就是其this.get調用
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this);
}
};
//在該方法中會觸發subs的update方法
Dep.prototype.notify = function notify () {
// 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();
}
};
3、watch
初始化watch,函數中會調用createWatcher,createWatcher會調用$watch,$watch調用new Watcher實例。
function initWatch (vm, watch) { for (var key in watch) { var handler = watch[key]; 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 ) { if (isPlainObject(handler)) { options = handler; handler = handler.handler; } if (typeof handler === 'string') { handler = vm[handler]; } return vm.$watch(expOrFn, handler, options) } 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); 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(); } }; }
2、computed
初始化computed,調用new Watcher(),並通過defineComputed函數將計算屬性掛載到vue實例上,使計算屬性可以在模板中使用
var computedWatcherOptions = { lazy: true }
function initComputed (vm, computed) { // $flow-disable-line var watchers = vm._computedWatchers = Object.create(null); // computed properties are just getters during SSR var isSSR = isServerRendering(); for (var key in computed) { var userDef = computed[key]; var getter = typeof userDef === 'function' ? userDef : userDef.get;
//getter也就是computed的函數 if (getter == null) { warn( ("Getter is missing for computed property \"" + key + "\"."), vm ); } if (!isSSR) { // create internal watcher for the computed property. 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); } } } }
function defineComputed (
target,
key,
userDef
) {
var shouldCache = !isServerRendering();//true
if (typeof userDef === 'function') {
sharedPropertyDefinition.get = shouldCache
? createComputedGetter(key)
: createGetterInvoker(userDef);
sharedPropertyDefinition.set = noop;
} else {
sharedPropertyDefinition.get = userDef.get
? shouldCache && userDef.cache !== false
? createComputedGetter(key)
: createGetterInvoker(userDef.get)
: noop;
sharedPropertyDefinition.set = userDef.set || noop;
}
if (sharedPropertyDefinition.set === noop) {
sharedPropertyDefinition.set = function () {
warn(
("Computed property \"" + key + "\" was assigned to but it has no setter."),
this
);
};
}
Object.defineProperty(target, key, sharedPropertyDefinition);
}
//computed的getter函數,在模板獲取對應computed數據時會調用
function createComputedGetter (key) {
return function computedGetter () {
var watcher = this._computedWatchers && this._computedWatchers[key];
if (watcher) {
if (watcher.dirty) {//true
watcher.evaluate();//該方法會調用watcher.get方法,也就是computed對應的函數
}
if (Dep.target) {
watcher.depend();
}
return watcher.value
}
}
}
通過以上代碼可以看到watch和computed都是通過new Watcher實例實現數據的監聽的,但是computed的options中lazy為true,這個參數導致它們走的是兩條不同路線。
computed:模板獲取數據時,觸發其getter函數,最終調用watcher.get,也就是調用對應回調函數。
watch:模板獲取數據時,觸發其getter函數,將watcher添加到對應的Dep.subs中,在之後setter被調用時,Dep.notify通知所有watcher進行update,最終調用watcher.cb,也就是調用對應回調函數。
3、Watcher
構造函數在是watch時,會最後調用this.get,會觸發屬性的getter函數,將該Watcher添加到Dep的subs中,用於通知數據變動時調用。
調用Watcher實例的update方法會觸發其run方法,run方法中會調用觸發函數。其depend方法會調用new Dep的depend方法,dep的depend會調用Watcher的addDep方法,最終會把該watcher實例添加到Dep的subs屬性中
/** *觀察者解析表達式,收集依賴項, *併在表達式值更改時激發回調。 *這用於$watch()api和指令。 */ var Watcher = function Watcher ( vm, expOrFn, cb, options, isRenderWatcher ) { this.vm = vm; ...... this.cb = cb;//觸發函數 this.id = ++uid$2; // uid for batching this.active = true; this.dirty = this.lazy; // for lazy watchers ...... this.value = this.lazy ? undefined ? this.get();//computed會返回undefined,而watch會執行Watcher.get }; /** * Scheduler job interface. * Will be called by the scheduler. * 該方法會執行觸發函數 */ Watcher.prototype.run = function run () { if (this.active) { var value = this.get(); if ( value !== this.value || // Deep watchers and watchers on Object/Arrays should fire even // when the value is the same, because the value may // have mutated. isObject(value) || this.deep ) { // set new value var oldValue = this.value; this.value = value; if (this.user) { try { this.cb.call(this.vm, value, oldValue); } catch (e) { handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\"")); } } else { this.cb.call(this.vm, value, oldValue); } } } }; /** * Evaluate the getter, and re-collect dependencies. */ 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 }; /** * Subscriber interface. * Will be called when a dependency changes. * 在方法中調用Watcher的run方法 */ Watcher.prototype.update = function update () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true; } else if (this.sync) { this.run(); } else { queueWatcher(this);//該方法最終也會調用run方法 } }; /** * Depend on all deps collected by this watcher.會調用new Dep的depend方法,dep的depend會調用Watcher的addDep方法 */ Watcher.prototype.depend = function depend () { var i = this.deps.length; while (i--) { this.deps[i].depend(); } }; /** * Add a dependency to this directive. */ 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); } } };