前言:此文章主要是分析Vue的響應(yīng)式原理,從new Vue開始,看源碼究竟發(fā)生了什么,其中會(huì)忽略掉大量邊界處理以及“不重要”的代碼,最后再驗(yàn)證一下,建議可以自己clone源碼,然后跟著文中的步驟跟著看,文中對(duì)代碼不會(huì)有太多講解,源碼本身的命名結(jié)構(gòu)都是很清晰的(在去除邊界處理代碼之后),主要是記錄過(guò)程,幫助以后自己回憶,如果你是對(duì)響應(yīng)式原理完全不懂的,建議可以先看看Introduction - Advanced Components | Vue Mastery或者Vue 3 Reactivity - Vue 3 Reactivity | Vue Mastery
看源碼!
- src/core/index.js中聲明了Vue函數(shù)
function Vue(options) { this._init(options); }
// 設(shè)置Vue.prototype._init
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
- 接著來(lái)到src/core/instance/init.js,在initMixin中我們可以找到Vue中_init函數(shù)的聲明,它主要是初始化了Vue組件的聲明周期、事件、Render,然后觸發(fā)了beforeCreate的鉤子,再接著初始化Injections、Provide,再觸發(fā)了created的鉤子,最后掛載組件
export function initMixin (Vue: Class<Component>) {
Vue.prototype._init = function(options?: Object) {
const vm: Component = this;
// ...Normalizing options ...
vm._self = vm;
initLifecycle(vm);
initEvents(vm);
initRender(vm);
callHook(vm, 'beforeCreate');
initInjections(vm);
initState(vm);
initProvide(vm);
callHook(vm, 'created');
// ...
vm.$mount(vm,$options.el);
}
}
- 這其中我們需要關(guān)注的就是initState,來(lái)到src/core/instance/state.js
export function initState (vm: Component) {
vm._watchers = [];
const 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);
}
}
可以看到,在initState中主要是初始化Props、Methods、Data、Computed(計(jì)算屬性)和Watch(偵聽器)
- 我們要關(guān)注的是initData,initData還是在state.js中
function initData (vm: Component) {
let data = vm.$options.data;
// ...
observe(data, true /* asRootData */);
}
- 緊接著是observe(src/core/observer/index.js)
/**
* 嘗試為value創(chuàng)建一個(gè)observer實(shí)例,并返回這個(gè)observer
*/
export function observe(value: any, asRootData: ?boolean): Observer | void {
// ...
ob = new Observer(value);
return ob;
}
- 繼續(xù)在src/core/observer/index.js找到Observer的Class聲明
export class Observer {
value: any;
dep: Dep;
constructor (value: any) {
this.value = value;
// ...
if (Array.isArray(value)) {
// ...
this.observeArray(value);
} else {
this.walk(value);
}
}
walk (obj: Object) {
const keys = Object.keys(obj);
for (let i = 0; i< keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]]);
}
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[I]);
}
}
}
export function defineReactive (...) {
const dep = new Dep();
// ...
let childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
get: function reactiveGetter () { dep.depend(); },
set: function reactiveSetter (newVal) { dep.notify(); }
});
// ...
}
- 下面要看看Dep是如何定義的了,來(lái)到src/core/observer/dep.js
export default class Dep {
static target: ?Watcher;
subs: Array<Watcher>;
constructor () {
this.subs = [];
}
addSub (sub: Watcher) {
this.subs.push(sub);
}
depend () {
if (Dep.tartget) {
Dep.target.addDep(this);
}
}
notify () {
const subs = this.subs.slice();
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
}
}
Dep.target = null
const targetStack = []
export function pushTarget (_target: Watcher) {
if (Dep.target) targetStack.push(Dep.target)
Dep.target = _target
}
export function popTarget () {
Dep.target = targetStack.pop()
}
- 最后就是Watcher,讓我們把目光轉(zhuǎn)移到src/core/observer/watcher.js
import Dep, { pushTarget, popTarget } from './dep'
export deafult class Watcher {
vm: Component;
newDepIds: ISet;
getter: Function;
value: any;
constructor (vm: Component, expOrFn: string | Function ...) {
// ...
this.vm = vm;
this.getter = expOrFn;
// ...
this.value = this.get();
}
get () {
pushTarget(this);
const vm = this.vm;
let value = this.getter.call(vm, vm);
// ...
popTarget();
return value;
}
update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true
} else if (this.sync) {
this.run()
} else {
queueWatcher(this)
}
}
}
梳理一下
總的來(lái)理一遍:
首先,new Vue會(huì)調(diào)用initState,為data創(chuàng)建一個(gè)Observer類的實(shí)例,同時(shí)遞歸為data中的Object類型的property創(chuàng)建一個(gè)Observer類的實(shí)例(如果這些property中還有Object類型的屬性,則繼續(xù)遞歸創(chuàng)建Observer),并為每一個(gè)Observer都創(chuàng)建了一個(gè)Dep類的實(shí)例;
然后使用了Object.defineProperty來(lái)攔截并重寫property的get和set,在defineProperty中為每個(gè)property都創(chuàng)建了一個(gè)Dep類的實(shí)例,再在get和set中分別調(diào)用了dep.depend()和dep.notify()用于將當(dāng)前的Watcher添加到dep的訂閱列表中和觸發(fā)訂閱列表中Wacther的updat方法;而Watcher才是真正記錄了使用響應(yīng)式數(shù)據(jù)的函數(shù)的類
驗(yàn)證一下
來(lái)看一個(gè)簡(jiǎn)單的例子:
<template>
<div>
<h1>{{ obj.name }}</h1>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
obj1: {
name: "test",
obj2: {
test: 'test'
},
},
};
},
mounted() {
console.log(this.obj);
},
};
</script>
來(lái)看看控制臺(tái)的打?。?/p>
首先,data中的obj是有一個(gè)ob指向它的Observer的(data也有,但并沒(méi)有直接有一個(gè)ob屬性指向它的Observer)
-
然后我們也能看到obj中的obj也是有一個(gè)ob屬性的,同樣指向它的Observer,同時(shí)也能看到每一個(gè)Observer都對(duì)應(yīng)的有一個(gè)Dep
-
接著就是確認(rèn)get和set中的dep,下面是get name和set name中的dep,可以通過(guò)id確認(rèn)這兩個(gè)dep是同一個(gè)dep,均為defineProperty時(shí)閉包中的dep
至此響應(yīng)式原理分析和驗(yàn)證告一段落,最后放一張圖幫助理解
14F77938-0913-4378-99E6-04B75BE27E5B.png



