大致流程
- 發(fā)生在beforeCreate和created之間initState(vm)中的defineProperty
- 發(fā)生在beforeMount和mounted之間的Dep和Watcher的初始化
- 發(fā)生在beforeUpdate前到updated觸發(fā)厘托,這期間Watcher的相關(guān)變化
第一步:數(shù)據(jù)初始化
在new一個(gè)Vue實(shí)例時(shí)澄干,其實(shí)只執(zhí)行了一個(gè)this._init(options)
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword');
}
this._init(options);
}
在_init方法中实辑,包含以下這些操作,其中initState包含對(duì)data和props的處理過(guò)程;initData包含了對(duì)data創(chuàng)建觀察者的observe函數(shù)
Vue.prototype._init = function (options) {
...
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm);
initState(vm);
initProvide(vm);
callHook(vm, 'created');
...
}
function initState (vm) {
...
if (opts.data) {
initData(vm);
} else {
observe(vm._data = {}, true);
...
}
function initData (vm) {
var data = vm.$options.data;
data = vm._data = typeof data === 'function' // 這行代碼解釋了平時(shí)為啥data為什么支持函數(shù)式的
? getData(data, vm)
: data || {};
...
proxy(vm, "_data", key);// 將data綁定到vue的this上
...
observe(data, true);
}
這里observe(data)會(huì)return一個(gè)Observe類(lèi)的實(shí)例
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++;
}
return ob
}
Observe類(lèi)將傳進(jìn)來(lái)的參數(shù)進(jìn)行遞歸調(diào)用杨拐,最終都會(huì)調(diào)用this.walk
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);
}
};
Observer.prototype.walk = function walk (obj) {
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
defineReactive$$1(obj, keys[i]);
}
};
終于看見(jiàn)和definePropoty長(zhǎng)得差不多的defineReactive棕所,其實(shí)defineReactive就是創(chuàng)建響應(yīng)式對(duì)象,是對(duì)definePropoty的一層封裝辨宠,到這里響應(yīng)式數(shù)據(jù)的初始化就算完成了遗锣,完整代碼如下:
function defineReactive$$1 (
obj,
key,
val,
customSetter,
shallow
) {
var dep = new Dep();
var property = Object.getOwnPropertyDescriptor(obj, key);
if (property && property.configurable === false) {
return
}
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;
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;
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter();
}
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
});
}
第二步:建立依賴(lài)
建立依賴(lài)其實(shí)就是觸發(fā)Object.defineProperty中定義get的一個(gè)過(guò)程,我們都知道get是在獲取對(duì)象值的時(shí)候觸發(fā)的函數(shù)嗤形,在vue運(yùn)行過(guò)程中精偿,get的觸發(fā)是在beforeMount和mounted這兩個(gè)聲明周期之間,這里就不去羅列模板解析過(guò)程了赋兵,大致就是一個(gè)template => AST => render函數(shù) => Vnode => DOM的過(guò)程笔咽,這里接著最上面created聲明周期后的部分進(jìn)行,執(zhí)行了$mount
Vue.prototype._init = function (options) {
initState(vm);
initProvide(vm);
callHook(vm, 'created');
if (vm.$options.el) {
vm.$mount(vm.$options.el);
}
}
Vue.prototype.$mount = function (
el,
hydrating
) {
el = el && inBrowser ? query(el) : undefined;
return mountComponent(this, el, hydrating)
};
function mountComponent (
vm,
el,
hydrating
) {
vm.$el = el;
...
callHook(vm, 'beforeMount');
...
// 這里有一段updateComponent的定義
...
new Watcher(vm, updateComponent, noop, {
before: function before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
}
}, true );
...
if (vm.$vnode == null) {
vm._isMounted = true;
callHook(vm, 'mounted');
}
return vm
}
從上圖可以看出beforeMount和mounted之間其實(shí)就定義了一個(gè)名為updateComponent(它是Watcher里的一個(gè)回調(diào)霹期,發(fā)生在Watcher的get中)叶组,然后new了一個(gè)Watcher。
這里主要講講Dep和Watcher历造,先介紹Watcher和Watcher是如何作為target定義到Dep上的:
var Watcher = function Watcher (
vm,
expOrFn,
cb,
options,
isRenderWatcher
) {
this.vm = vm;
if (isRenderWatcher) {
vm._watcher = this;
}
vm._watchers.push(this);
if (options) {
// mounted階段new的那個(gè)Watcher里只有before字段甩十,其他初始化全都是false
this.deep = !!options.deep;
this.user = !!options.user;
this.lazy = !!options.lazy;
this.sync = !!options.sync;
this.before = options.before;
} else {
this.deep = this.user = this.lazy = this.sync = false;
}
this.cb = cb;
this.id = ++uid$2; // uid for batching
this.active = true;
this.dirty = this.lazy; // for lazy watchers
this.deps = [];
this.newDeps = [];
this.depIds = new _Set();
this.newDepIds = new _Set();
this.expression = process.env.NODE_ENV !== 'production'
? expOrFn.toString()
: '';
if (typeof expOrFn === 'function') {
this.getter = expOrFn;
} else {
this.getter = parsePath(expOrFn);
if (!this.getter) {
this.getter = noop;
process.env.NODE_ENV !== 'production' && warn(
"Failed watching path: \"" + expOrFn + "\" " +
'Watcher only accepts simple dot-delimited paths. ' +
'For full control, use a function instead.',
vm
);
}
}
this.value = this.lazy
? undefined
: this.get();
};
上面的最后調(diào)用了this.get()船庇,在get()函數(shù)里利用pushTarget把Watcher 作為 target 定義在了Dep上,并且執(zhí)行了this.getter.call(vm, vm);這里的getter是Watcher構(gòu)造函數(shù)的第二個(gè)參數(shù)expOrFn侣监,內(nèi)容為vm._update(vm._render(), hydrating)鸭轮,也就是觸發(fā)了頁(yè)面的渲染
function pushTarget (target) {
targetStack.push(target);
Dep.target = target;
}
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 {
if (this.deep) {
traverse(value);
}
popTarget();
this.cleanupDeps();
}
return value
};
在vm._update的渲染過(guò)程中,因?yàn)橐昧薲ata中的數(shù)據(jù)橄霉,所以會(huì)觸發(fā)第一階段中defineProperty為data內(nèi)數(shù)據(jù)設(shè)置的get函數(shù)窃爷,代碼如下:如果Dep.target存在會(huì)調(diào)用dep.depend()(Dep.target其實(shí)是一個(gè)Watcher)
function defineReactive$$1 (){
Object.defineProperty(obj, key, {
...
var dep = new Dep();
...
get: function reactiveGetter () {
var value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
...
}
}
}
}
再看看Dep類(lèi),在defineReactive中會(huì)new一個(gè)Dep的實(shí)例酪劫,這里subs是一個(gè)裝watcher的數(shù)組吞鸭,一般在不自定義watch的前提下,這個(gè)數(shù)組里都只有一個(gè)Watcher
var Dep = function Dep () {
this.id = uid++;
this.subs = [];
};
Dep.prototype.depend = function depend () {
if (Dep.target) {
Dep.target.addDep(this); // 根據(jù)上面的描述這里Dep.target就是Watcher
}
};
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);
}
}
};
Dep.prototype.addSub = function addSub (sub) {
this.subs.push(sub);
};
忽視掉和響應(yīng)式數(shù)據(jù)無(wú)關(guān)的部分覆糟,到這里基本就是mount結(jié)束的地方了刻剥,總結(jié)下都干了什么,觸發(fā)beforeMount生命周期滩字,new了一個(gè)Watcher對(duì)象造虏,渲染模板,觸發(fā)數(shù)據(jù)的get初始化麦箍,對(duì)每個(gè)響應(yīng)式數(shù)據(jù)的Dep實(shí)例進(jìn)行依賴(lài)收集漓藕,然后觸發(fā)Mounted生命周期。
第三步:派發(fā)更新
當(dāng)有響應(yīng)式的數(shù)據(jù)被改變時(shí)挟裂,觸發(fā)set函數(shù)享钞,調(diào)用dep.notify()
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val;
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter();
}
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
這里subs就是一個(gè)裝Watcher的數(shù)組(在沒(méi)有綁定自定義Watcher的簡(jiǎn)單的Vue對(duì)象中,這個(gè)數(shù)組的長(zhǎng)度是1)诀蓉,所以就等于是調(diào)用了當(dāng)前vue對(duì)象對(duì)應(yīng)Watcher的update()
Dep.prototype.notify = function notify () {
var subs = this.subs.slice();
if (process.env.NODE_ENV !== 'production' && !config.async) {
subs.sort(function (a, b) { return a.id - b.id; });
}
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
Watcher的update()會(huì)對(duì)根據(jù)Watcher初始化傳入的option中sync字段進(jìn)行一個(gè)判斷栗竖,如果是true的直接觸發(fā)run(),如果不是會(huì)進(jìn)行一個(gè)隊(duì)列的操作渠啤。因?yàn)槲覀冊(cè)?mount過(guò)程中new Watcher時(shí)傳的option只有before字段狐肢,所以其他lazy,sync等字段都是false沥曹,所以這里會(huì)產(chǎn)生一個(gè)隊(duì)列份名,用于存放Watcher
Watcher.prototype.update = function update () {
if (this.lazy) { //這里是false
this.dirty = true;
} else if (this.sync) { // 這里是false
this.run();
} else {
queueWatcher(this);
}
};
這個(gè)隊(duì)列會(huì)先判斷之前是否添加過(guò)這個(gè)watcher,如果沒(méi)有則添加妓美,并會(huì)有一個(gè)針對(duì)id的排序插入
function queueWatcher (watcher) {
var id = watcher.id;
if (has[id] == null) {
has[id] = true;
if (!flushing) {
queue.push(watcher);
} else {
var i = queue.length - 1;
while (i > index && queue[i].id > watcher.id) {
i--;
}
queue.splice(i + 1, 0, watcher);
}
if (!waiting) {
waiting = true;
if (process.env.NODE_ENV !== 'production' && !config.async) {
flushSchedulerQueue();
return
}
nextTick(flushSchedulerQueue);
}
}
}
flushSchedulerQueue僵腺,首先會(huì)對(duì)隊(duì)列中的Watcher進(jìn)行排序,然后觸發(fā)option中的before壶栋,也就是beforeUpdate的生命周期函數(shù)想邦,然后執(zhí)行Watcher.run()
function flushSchedulerQueue () {
queue.sort(function (a, b) { return a.id - b.id; });
...
for (index = 0; index < queue.length; index++) {
watcher = queue[index];
if (watcher.before) {
watcher.before();
}
/**
before () {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate');
}
}
**/
id = watcher.id;
has[id] = null;
watcher.run();
}
...
// 下面就不介紹了。委刘。丧没。
// 隊(duì)列的備份
var activatedQueue = activatedChildren.slice();
var updatedQueue = queue.slice();
// 隊(duì)列的初始化
resetSchedulerState();
// 觸發(fā)activated和updated的生命周期函數(shù)
callActivatedHooks(activatedQueue);
callUpdatedHooks(updatedQueue);
}
run的時(shí)候觸發(fā)get(),會(huì)和首次mount過(guò)程類(lèi)似锡移,多了patch的過(guò)程呕童,其中涉及著名的Diff算法,用于渲染頁(yè)面淆珊,從而更新頁(yè)面夺饲,并建立新的依賴(lài)關(guān)系
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);
}
}
}
};
完~
大致流程就是這樣了,似乎寫(xiě)的有點(diǎn)亂施符,如有問(wèn)題歡迎大佬們指正