在Observer源碼部分主要有三個角色:Observer、Dep、Watcher。node
看這一部分,能夠帶着如下幾個問題來看:react
一、知足什麼條件能夠將其變成響應式的數組
二、Observer是如何去分別處理傳入的數組或者對象的?ui
三、有兩處new Dep,做用分別是什麼?this
四、核心代碼defineReactive幹了些什麼?spa
五、Dep.target是什麼?defineReactive中get時,爲何要判斷Dep.target?code
function observe (value, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
let 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
}
複製代碼
其實上述代碼中有不少判斷,咱們能夠得出如下結論:cdn
(1)必須是一個對象,且不能是vnode的類型的。server
if (Array.isArray(value)) {
if (hasProto) {
protoAugment(value, arrayMethods)
} else {
copyAugment(value, arrayMethods, arrayKeys)
}
this.observeArray(value)
} else {
this.walk(value)
}
複製代碼
這段代碼就是判斷傳入的值是否是數組,若是是數組,走observeArray方法,若是不是數組,那麼走walk方法。對象
walk (obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
observeArray (items) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
複製代碼
walk這個方法就是遍歷對象,而後給對象中的屬性值變成響應式的,遍歷完以後,整個對象就是響應式對象了。 observeArray這個方法是遍歷數組,而後對數組中每個元素在走一遍響應式流程。
第一處:
class Observer {
constructor (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)
}
}
複製代碼
第二處:
function defineReactive ( obj, key, val, customSetter, shallow ) {
const dep = new Dep()
// 省略中間代碼
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
// 省略中間代碼
}
return value
},
set: function reactiveSetter (newVal) {
// 省略中間代碼
dep.notify()
}
})
}
複製代碼
第二處dep咱們很好理解,是給get和set服務的,進行依賴收集和派發更新。
第一處dep,咱們能夠考慮一下,他是整個對象的一個屬性,那麼他什麼時候進行依賴收集和派發更新。
咱們能夠全局搜一下dep.depend。發現會有三處。有兩處是對ob屬性進行操做的,也就是對整個對象進行依賴收集。
在全局搜一下dep.notify。發現有四處。有三處是對ob屬性進行操做的。分別是set和del,數組的一個方法。
這一部分代碼能夠分爲三部分看:定義一些變量、Object.defineProperty、對childOb操做。
// 生成一個新的dep。
const dep = new Dep()
// 判斷這個對象這個屬性是否能夠修改
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// 定義getter和setter方法
const getter = property && property.get
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {
val = obj[key]
}
複製代碼
get: function reactiveGetter () {
// 獲取值
const value = getter ? getter.call(obj) : val
// 當存在Dep.target的時候進行依賴收集
if (Dep.target) {
dep.depend()
// 省略中間代碼
}
// 返回獲取到的值
return value
},
set: function reactiveSetter (newVal) {
// 獲取原來的值
const value = getter ? getter.call(obj) : val
// 將新的值和原值進行對比,若是沒有發生改變,就直接返回
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
// 該屬性不能set的狀況也直接返回
if (getter && !setter) return
// 給該屬性賦值
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// 從新對這個值進行監聽
childOb = !shallow && observe(newVal)
// 更新dep中的watcher
dep.notify()
}
複製代碼
// 嘗試將值轉化成響應式對象
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) {
// 省略中間代碼
//由於值發生了變更,因此再一次嘗試將其變成一個響應式對象
childOb = !shallow && observe(newVal)
}
})
複製代碼
其實Dep.target是一個全局變量,更是一個wathcer。在dep文件中的源碼以下:
Dep.target = null
const targetStack = []
export function pushTarget (target: ?Watcher) {
targetStack.push(target)
Dep.target = target
}
export function popTarget () {
targetStack.pop()
Dep.target = targetStack[targetStack.length - 1]
}
複製代碼
由於全部定義在data中的值,都會被變成響應式對象,可是每個不必定有watcher。watcher分爲三種:render中生成的watcher、用戶自定義的watcher、computed。
上述流程中,當生成一個新的renderWatcher的時候,便會走get流程,而後進行依賴收集,若是沒有Dep.target,說明這個值並無對應的watcher,因此不須要進行依賴收集。 當更新的是時候,又回進行一次依賴收集。