上次講解,大體介紹了下目前的調試環境,若是沒有看過的同窗能夠看上一篇文章。爲了弄明白咱們標題中所描述的問題,我先帶你們看下vue中的響應式是怎樣實現的。vue
首先來看一下咱們本次的代碼案例:node
在DOM中定義了一個vue事例的data變量,2秒鐘以後改變變量的字符串值。數組
在實例化vue的時候,會執行_init(options)方法。在_init方法中,涉及到了vue實例的初始化邏輯,在initState方法中運行了initData方法,通過一系列檢查執行了 observe(data, true /* asRootData */)。在observe方法中,實例化了一個observer對象 ob = newObserver(value)。閉包
在實例化observer對象的中:app
給observer對象本身添加了一個dep屬性,爲Dep對象。ide
而後將observer對象自身掛載到傳入的待觀察value對象中,屬性值爲__ob__。函數
以後對傳入的value對象執行了Observer原型上的walk方法,walk方法對value中的每一個屬性執行了defineReactive方法 defineReactive(obj, keys[i])。defineReactive方法對每一個屬性繼續執行了observe方法,而後經過defineProperty繼續對每一個屬性設置get和set屬性描述。spa
下面來重點講解下defineReactive函數中的get。
調試
在剛進入defineReactive函數時實例化了一個dep實例。orm
在get函數中經過dep.depend作依賴收集。
對響應式對象的每一個屬性執行defineReactive的時候,每一個屬性都會有本身的dep實例。而且在get屬性描述中執行了dep.depend,也就是在渲染Vnode時,只要讀取了對象的響應式屬性,就會執行這個屬性的get函數。
Dep即dependence依賴,也就是把每一個對象的屬性做爲一個渲染Vue實例時的依賴。那麼Vue實例咱們經過什麼類來表示他和數據依賴的關係呢?就是Watcher,上次講解到mountComponent中渲染和掛載時會實例化一個Watcher類,Watcher類中的操做其實就是把本身保存到一個全局的變量中,而且push到一個全局的棧中。以後來執行傳入的回調函數來實現渲染Vnode和掛載。
在渲染Vnode的時候,就能夠讀取到響應式對象的屬性,進而執行屬性描述get函數。在get函數中,執行了保存在響應式屬性閉包中dep實例的depend方法。此函數就是在剛纔保存的全局watcher中添加當前的這個響應式屬性閉包中的dep做爲依賴,這就是依賴收集。而且在這個依賴屬性的dep中加入了當前正在渲染的watcher。
接下來說defineReactive中的set屬性描述,set函數中起到通知相應的vue實例更新的做用的函數就是dep.notify()函數。即更新這個dep中被訂閱的全部watcher。
那麼迴歸正題,在vue2對數組執行observe函數時又會發生什麼?
在observe函數中檢查了傳入的參數是不是數組或對象,不是則直接返回。以後實例化observer對象。
在實例化Observer類時,如上圖會先判斷是不是數組,若是是數組,最後會執行observeArray函數,若是不是數組會執行walk函數。在walk函數中逐個屬性執行defineReactive來定義set和get函數,可是observeArray函數中
則只是對每一個屬性繼續執行observe,並無對屬性自己經過defineReactive函數來添加屬性描述get或set。
下面來看一下若是咱們定義data爲對象時,最後的data變量:
對比data爲數組時的data變量:
在數組這一層中並無定義每一個元素的get和set屬性,因此是不能直接用索引實現數組的響應式的。