在概述中說到,Vue2.5的數(shù)據(jù)綁定主要是通過對象屬性的 getter 和 setter 的鉤子來實現(xiàn)的,總的來說是在初始化的時候建立起鉤子,并且在數(shù)據(jù)更新的時候通知相應(yīng)的 Watcher 去運行回調(diào)函數(shù)執(zhí)行更新等操作。
具體來講,要分以下幾步:
初始化實例對象時運行initState, 建立好props, data 的鉤子以及其對象成員的Observer, 對于computed 屬性,則建立起所有對應(yīng)的 Watcher 并且通過 Object.defineProperty 在vm對象上設(shè)置一個該屬性的 getter。同時還根據(jù)自定義的 watch 來建立相應(yīng)的 Watcher
執(zhí)行掛載操作,在掛載時建立一個直接對應(yīng)render(渲染)的 Watcher,并且編譯模板生成 render 函數(shù),執(zhí)行vm._update 來更新 DOM 。
此后每當(dāng)有數(shù)據(jù)改變,都將通知相應(yīng)的 Watcher 執(zhí)行回調(diào)函數(shù)。
而想要真正理解這里的邏輯, 則需要先搞清楚 Dep, Observer 和 Watcher 這三種對象的作用,以及定義的鉤子函數(shù)中究竟做了什么。
Dep (dependency // 依賴)
位置: src/core/observer/dep.js
class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>;
constructor () {
this.id = uid++
this.subs = []
}
addSub (sub: Watcher) {
this.subs.push(sub)
}
removeSub (sub: Watcher) {
remove(this.subs, sub)
}
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
Dep 就是一個 Watcher 所對應(yīng)的數(shù)據(jù)依賴,在這個對象中也存有一個 subs 數(shù)組,用來保存和這個依賴有關(guān)的 Watcher。其成員函數(shù)最主要的是 depend 和 notify ,前者用來設(shè)置某個 Watcher 的依賴,后者則用來通知與這個依賴相關(guān)的 Watcher 來運行其回調(diào)函數(shù)。
另外還可以看到的是,這個類還有一個靜態(tài)成員 target, 同時還有一個全局的棧,其中儲存的是正在運行的Watcher。
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()
}
這里的壓棧和出棧的方法就不用多說了。
Observer
位置: src/core/observer/index.js
class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that has this object as root $data
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (Array.isArray(value)) {
const augment = hasProto
? protoAugment
: copyAugment
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
this.walk(value)
}
}
/**
* Walk through each property and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
/**
* Observe a list of Array items.
*/
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
Observer 主要是用來監(jiān)視一個對象的變化,比如在 data 中存在一個對象成員,直接給該對象成員添加屬性并不會觸發(fā)任何鉤子函數(shù),但是這個對象又是數(shù)據(jù)的一部分,也就是說該對象發(fā)生變化也會導(dǎo)致DOM發(fā)生改變,因此要用 Observer 來監(jiān)視一個對象的變化并且在變化時通知與其相關(guān)的 Watcher 來運行回調(diào)函數(shù)。
可以看到,Observer 中存在一個 Dep,也就是一個依賴。
在建立 Observer 的時候會用到 observe 函數(shù)
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {// 判斷該對象是否已經(jīng)存在 Observer
ob = value.__ob__
} else if (
observerState.shouldConvert &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
這個函數(shù)主要是用來動態(tài)返回一個 Observer,首先判斷value如果不是對象則返回,然后檢測該對象是否已經(jīng)有 Observer,有則直接返回,否則新建并將 Observer 保存在該對象的 ob 屬性中(在構(gòu)造函數(shù)中進行)。
在建立 Observer 時,如果OB的是數(shù)組則對數(shù)組中每個成員執(zhí)行 observe 函數(shù),否則對每個對象屬性執(zhí)行 defineReactive 函數(shù)來設(shè)置 get set 鉤子函數(shù)。
Watcher
位置: src/core/observer/watcher.js
class Watcher {
vm: Component;
expression: string;
cb: Function;
id: number;
deep: boolean;
user: boolean;
lazy: boolean;
sync: boolean;
dirty: boolean;
active: boolean;
deps: Array<Dep>;
newDeps: Array<Dep>;
depIds: SimpleSet;
newDepIds: SimpleSet;
getter: Function;
value: any;
constructor (
vm: Component,
expOrFn: string | Function,
cb: Function,
options?: ?Object,
isRenderWatcher?: boolean
) {
this.vm = vm
if (isRenderWatcher) {
vm._watcher = this
}
vm._watchers.push(this)
// options
if (options) {
this.deep = !!options.deep
this.user = !!options.user
this.lazy = !!options.lazy
this.sync = !!options.sync
} else {
this.deep = this.user = this.lazy = this.sync = false
}
this.cb = cb
this.id = ++uid // 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()
: ''
// parse expression for getter
if (typeof expOrFn === 'function') {
this.getter = expOrFn
} else {
this.getter = parsePath(expOrFn)
if (!this.getter) {
this.getter = function () {}
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()
}
/**
* Evaluate the getter, and re-collect dependencies.
*/
get () {
......
}
/**
* Add a dependency to this directive.
*/
addDep (dep: Dep) {
......
}
/**
* Clean up for dependency collection.
*/
cleanupDeps () {
......
}
/**
* Subscriber interface.
* Will be called when a dependency changes.
*/
update () {
......
}
/**
* Scheduler job interface.
* Will be called by the scheduler.
*/
run () {
......
}
/**
* Evaluate the value of the watcher.
* This only gets called for lazy watchers.
*/
evaluate () {
......
}
/**
* Depend on all deps collected by this watcher.
*/
depend () {
......
}
/**
* Remove self from all dependencies' subscriber list.
*/
teardown () {
......
}
}
Watcher 在構(gòu)造時傳入的參數(shù)最重要的是 expOrFn , 這是一個 getter 函數(shù),或者可以用來生成一個 getter 函數(shù)的字符串,而這個 getter 函數(shù)就是之前所說的回調(diào)函數(shù)之一另外一個回調(diào)函數(shù)是 this.cb,這個函數(shù)只有在用 vm.$watch 生成的 Watcher 才會運行。
getter 在 expOrFn 是字符串時,會運行 parsePath 取得其對應(yīng)的在 Vue 實例對象上的一個熟悉。
其中 get 函數(shù)的職責(zé)就是執(zhí)行 getter 函數(shù)并將可能的返回值(如果該 Watcher 是renderWatcher 則返回值是 undefined)賦值給該 Watcher 的 value 屬性。
update 函數(shù)則是在一個 Dep 通過 notify 函數(shù)通知 Watcher 后執(zhí)行的函數(shù),在其中執(zhí)行了 run 函數(shù),run 中又執(zhí)行了 get 函數(shù)。
depend 則是用來將 Watcher 中 deps 數(shù)組保存的所有依賴進行關(guān)聯(lián),使這些依賴在 notify 時可以通知到該 Watcher。
defineReactive
位置: src/core/observer/index.js
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const 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) {
const 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 (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
})
}
這個函數(shù)主要的職責(zé)是建立起某個對象屬性的get 和 set 鉤子,并且通過 observe 函數(shù)來獲取該對象的 Observer 對象,新建一個數(shù)據(jù)依賴 Dep。 在 get 鉤子函數(shù)中則去處理數(shù)據(jù)依賴和 Watcher 的關(guān)聯(lián),在 set 中調(diào)用依賴的 notify 函數(shù)通知關(guān)聯(lián)的 Watcher 去運行回調(diào)函數(shù)。
總結(jié)
到現(xiàn)在為止,已經(jīng)差不多理清了 Vue 數(shù)據(jù)綁定的大體邏輯,但是仍然還有很多遺留問題,比如:Dep 和 Watcher 是怎么建立聯(lián)系的,不同的 Watcher 都是在何時建立的,Watcher 的 getter 函數(shù)具體都有哪些,這些問題會在以后的文章中詳細(xì)說明。