initData--->observe(data)做的事情是:
data的__ob__指向一個observer,observer中有一個Dep
一、如果data是一個普通對象(非數組),遍歷其屬性(比如a),重寫a的get和set方法,get方法的閉包中有兩個重要屬性:
1、一個Dep類型的屬性(簡稱getDep)
2、遞歸observe(data.a)的返回值:Observer類型的childOb屬性,
當get方法被調用的時候,getDep+ childOb的dep收集棧頂的watcher,當set方法被調用的時候,收集的watcher被調用update方法
二、如果data是一個數組,因為data是根節點,會報錯,為了討論observe數組的情況,假定data.a的值是一個數組b,接着上面說的遞歸observe(b):
一樣的新建一個Observer,賦值給b的__ob__屬性,接下來發現b是數組,把b的__proto__屬性指向一個arrayMethods對象,arrayMethods的__proto__指向的是Array.prototype,重寫b的push等方法,重寫后的邏輯是先調用Array的push,再調用b的__ob__指向的
observer中的dep.notify。
data()方法的返回值以及watch,props,computed里面的屬性,,在這之前都會被defineReactive方法重寫get和set方法(get方法重寫為調用的時候收集Dep這個靜態變量指向的watcher,set方法調用的時候會notify收集的watchers,調用每個watcher的update--->run--->get方法)和methods里面的方法一起,被掛載到vm實例上(所以methods里面this.XXX才有用,因為調用method的是 vm,屬性也在vm上)
-----------------所以如果想要對象(比如a)的屬性b的set方法能引起重新渲染,必須在b被get的時候,收集到可以引起重新渲染的watcher。那么是什么時候收集的呢?
每一個vm有一個render Watcher,在mountComponent的時候生成,render Watcher的expOrFn方法是 vm._update(vm._render(), hydrating)--->vm.__patch__(prevVnode, vnode),(對比新舊虛擬節點,操作真實node),在這個過程中,vm._render()會按照Vue的<template>中的內容,為每一個子節點
生成屬性,查看render的代碼會發現有個with(this),這里this就是vm,就是要調用vm的屬性的get方法給虛擬節點的屬性賦值。然后再進行對比等操作。在這個過程中,被調用get方法的屬性就會收集renderWatcher。
-----------------那為什么array[0]='a'這樣的方法不會重新渲染?
因為array的0這個屬性?,沒有重寫get和set方法
-----------------為什么pop push可以reactive?
比如a.b=c,reactive的過程是: observe(a)---> defineReactive(b)--->重寫b的get方法之前先observe(c)。observe c的時候,如果判斷c是一個數組,protoAugment方法中,c的__proto__會指向arrayMethods這個對象,
而arrayMethods的__proto__指向Array.prototype,但是其push等屬性重寫了,先調用Array原來的相應方法等等一系列操作,再調用收集的watcher的update方法,如果收集了renderWatcher,就會reactive
-----------------對象的dep從哪里來?
上面說過,observe(c),new一個Observer 實例childOb,c的_ob_屬性指向childOb,childOb里面有一個dep屬性,而在b的重寫的get方法中,會有childOb.dep.depend()的執行
-----------------靈魂第五問:既然是給b重寫set和get的時候,給c的_ob_的dep收集Watcher,那么數組里面的元素比如array[0]=d,而d恰好是數組,為什么d.push也可以reactive?
在childOb.dep.depend()方法下面還有個if,如果屬性指向的是個數組,遍歷數組中的元素,使其observer的dep屬性收集renderWatcher。所以array[0]='a'是不能reactive的,但是array[0].push('a')是可以reactive的