上篇文章【源碼解析】建立Vue實例時幹了什麼?提到響應數據這個詞,到底什麼樣的數據是響應數據呢?node
小提示:配合源碼食用更佳美味。react
先講這樣一個過程。數組
在$mont()的時候,會建立Watcher實例的過程,把Dep.target設置爲當前Watcher,而後會開始render,render的時候就會讀取到響應數據,從而觸發get,只有被觀察的數據才配置了get,get執行過程當中會建立一個Dep實例,此時有了Watcher和Dep,他們會創建關係。他們創建關係以後,當一旦被觀察的數據發生改變,就會觸發set,set調用dep.notify(),dep則會讓跟他有關係的Watcher進行更新。緩存
被觀察的數據更改會致使組件進行更新從而影響到dom的改變。這個被觀察的數據就是響應數據,而這個get的過程咱們叫作依賴收集(後續分析)。性能優化
在init過程當中 咱們調用了initState -> initData閉包
src/core/instance/state.jsapp
// 觀察data
observe(data, true /* asRootData */)
複製代碼
看一下observe
函數的實現。進行各類判斷,最終返回Observer實例。dom
export function observe (value: any, asRootData: ?boolean): Observer | void {
// vnode和不是對象的不須要被觀察
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
// 性能優化:帶有__ob__的是已經被觀察的數據 直接返回value.__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
) {
// 須要被觀察的 直接建立Observer實例
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
複製代碼
因爲數組和普通對象的劫持方式不一樣,所以 新建一個Observer類進行來統一。函數
export class Observer {
value: any;
dep: Dep;
vmCount: number;
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
// __ob__賦值this,性能優化並表示數據已經被觀察
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 (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
// 定義響應數據 obj
defineReactive(obj, keys[i])
}
}
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
// 觀察每一項
observe(items[i])
}
}
}
複製代碼
對數組方法進行重寫, 而且響應數據源碼分析
src/core/observer/array.js
import { def } from '../util/index'
// 數組原型
const arrayProto = Array.prototype
// 建立新對象 不會對原數組產生影響
export const arrayMethods = Object.create(arrayProto)
// 常會對着7個方法進行劫持,僅有這7個方法會對數組改變
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method) {
// 緩存原來的方法
const original = arrayProto[method]
// 重寫方法
def(arrayMethods, method, function mutator (...args) {
// 調用原來的方法
const result = original.apply(this, args)
// 緩存
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
// 再次數據進行響應 確保修改後和新增的數據是響應的
if (inserted) ob.observeArray(inserted)
// 觸發更新 重中之重就是這個地方 依賴收集的時候分析
ob.dep.notify()
return result
})
})
複製代碼
調用了defineReactive(obj, keys[i])
export function defineReactive ( obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean ) {
const dep = new Dep()
// 這個對象可能設置過getter和setter,須要多調用一次用戶配置的
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
// 孩子是對象的遞歸觀察
let childOb = !shallow && observe(val)
// 劫持get和set 響應數據核心
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
// 先掉一下用戶的getter
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
// 同樣的值則不須要被賦值
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
if (getter && !setter) return
if (setter) {
// 用戶的setter
setter.call(obj, newVal)
} else {
// 賦值 閉包
val = newVal
}
// 被設置的須要進行觀察
childOb = !shallow && observe(newVal)
// 觸發更新
dep.notify()
}
})
}
複製代碼
感謝各位的閱讀,錯誤是在所不免的,如有錯誤,或者有更好的理解,請在評論區留言,再次感謝。但願你們相互學習,共同進步。