Vue 理解之白話 getter/setter詳解
當(dāng)你把一個(gè)普通的 JavaScript 對象傳給 Vue 實(shí)例的 data 選項(xiàng),Vue 將遍歷此對象所有的屬性,并使用 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter/setter 。Object.defineProperty 是 ES5 中一個(gè)無法 shim 的特性,這也就是為什么 Vue 不支持 IE8 以及更低版本瀏覽器
以上摘自 深入響應(yīng)式原理
那么,把這些屬性全部轉(zhuǎn)為 getter/setter 具體是怎樣一個(gè)過程呢?本文不深入具體 , 簡單大致了解其過程,旨在整體把握,理解其主要思路
假設(shè)代碼如下:

data 選項(xiàng)可以接收一個(gè)對象或者方法,這里以對象為例(其實(shí)最后都會轉(zhuǎn)為對象)
首先,這個(gè)對象的所有鍵值對都會被掛載在 vm._data 上(此外 vm._data 對象上還有個(gè) __ob__ key,暫時(shí)可以忽視),這樣我們便能用 vm._data.msg 訪問到數(shù)據(jù)
但是通常我們是用 vm.msg 這樣訪問數(shù)據(jù),如何做到的呢?其實(shí)就是做了個(gè)代理 , 將 data 鍵值對中的 vm[key] 的訪問都代理到 vm._data[key] 上

通常 vm._data (下劃線變量)用作內(nèi)部程序,對外暴露的 API 是 vm.$data , 其實(shí)這兩者也是一個(gè)東西 , 也是做了個(gè)代理,代碼大概這樣:

簡單理解就是訪問 vm.data.msg 其實(shí)就是訪問 vm._data.msg 。如果直接在開發(fā)環(huán)境對 vm.data = https://www.questions.com.cn/dnjc/xxx這樣的賦值 , 而不是vm.$data.msg = xxx` 這樣的賦值,后者是沒問題的)
至此,我們理解了為什么能用 vm.msg、vm._data.msg 以及 vm.$data.msg 三種方式獲取/改變數(shù)據(jù) , 最原始的數(shù)據(jù)是 vm._data.msg,而另外兩者即代理了 _data 的數(shù)據(jù),vm.$data.msg 即為 Vue 向外提供的 API , 一般情況下開發(fā)我們直接用 vm.msg 這樣比較多,也方便,如果要獲取整個(gè) data,程序中需要用 this.$data,而不是 this.data
接下來說 getter/setter
【Vue 理解之白話 getter/setter詳解】將 demo 稍微添點(diǎn)東西:

msg2 是依賴于 msg 的,當(dāng) msg 改變的時(shí)候,msg2 的值需要自動更新 , msg 的改變可以在 vm._data.msg 的 setter 中監(jiān)聽到,但是怎么知道 msg2 是依賴于 msg 的呢?
直觀地我們可以想到,遍歷所有 computed 對象的鍵值對,然后進(jìn)行分析 , 理論上似乎可行,但是我尋思著這可能需要解析 AST 啊,或者正則去匹配,看看是否用到了 this.msg,也可能是 this.$data.msg 啊 , 還可能是 this._data.msg,而且還要遍歷 data 中的所有 key,這看起來也太麻煩了吧 , 而且,如果程序中沒有用到 msg2,那不是多此一舉了?
事實(shí)上 , Vue 初始化的時(shí)候會對 vm._data 的每個(gè)鍵值對設(shè)置 getter/setter,大概代碼如下:

Vue 響應(yīng)式核心就是 , setter 的時(shí)候會收集依賴,getter 的時(shí)候會觸發(fā)依賴更新
我們還是以上面的 computed msg2 為例,當(dāng)我們第一次去取值 msg2 時(shí)(注意,必須是取值行為,可以是在 template , 也可以是程序中),勢必需要去取值 this.msg,這就會觸發(fā) msg 的 getter,此時(shí)我們就可以確定 msg2 依賴于 msg
msg 可以被哪些東西依賴呢?目前看來有三
template 模版中
computed
watch
我們可以打印 vm._watchers 查看,是一個(gè) Watcher 實(shí)例數(shù)組,直接看實(shí)例的 expression 值,其實(shí)就是觸發(fā)這個(gè)表達(dá)式的時(shí)候 , 會觸發(fā) msg 的 getter
而這個(gè)表達(dá)式就對應(yīng)上述的三種情況,因?yàn)?msg 改變的時(shí)候,這些表達(dá)式需要重新求值,所以這些依賴項(xiàng)都要保存起來,所以源碼中定于了這個(gè) Watcher 類
A watcher parses an expression, collects dependencies, and fires callback when the expression value changes. This is used for both the $watch() api and directives.
watcher.deps 數(shù)組表示該 watcher 的依賴項(xiàng) , 值為 Dep 實(shí)例,可以理解成和 Watcher 實(shí)例的表達(dá)式有關(guān)的 data 數(shù)據(jù) 。注意,deps 數(shù)組可能是空,對于 template 而言,可以是 template 中不依賴于 data,對于 computed 而言,可以是這個(gè) computed 數(shù)據(jù)還沒被獲?。ū熱縹葉ㄒ辶?msg2 , 但是程序中沒有用,這時(shí) deps 為空,這表明我如果改變了 msg,但是不需要通知到 msg2,因?yàn)?msg2 根本沒用到嘛,但是我在控制臺輸入 vm.msg2,從而觸發(fā)了 msg 的 getter,繼而進(jìn)行了依賴收集,這時(shí) deps 就不為空了 , 這表明我已經(jīng)使用了 msg2,下次 msg 更新時(shí)需要通知到 msg2 進(jìn)行改變)
而對于 watch 而言,我試了下任何情況下 deps 都不為空,這需要進(jìn)一步查看源碼確認(rèn)
deps 數(shù)組元素是 Dep 實(shí)例,該實(shí)例有個(gè) subs 屬性,是 Watcher 實(shí)例數(shù)組,表示依賴于這個(gè) Dep 的項(xiàng)目
Watcher 和 Dep 比較難理解,可以暫時(shí)這樣理解,Dep 和 data 掛鉤,每一個(gè) Dep 實(shí)例就對應(yīng) data 的一個(gè)鍵值對,Watcher 實(shí)例則依賴于 Dep,那么有三個(gè)情況會依賴 , 也就是以上三種(想想是不是這樣 , 當(dāng)數(shù)據(jù)更新的時(shí)候,是不是只有這三處需要同時(shí)更新,或者同時(shí)響應(yīng))
總結(jié)下:我們會對 data 中所有鍵值對設(shè)置 getter/setter,getter 的時(shí)候我們會收集依賴(依賴項(xiàng)為上面三項(xiàng),并不是任何情況下都會收集依賴,比如在鉤子中打印 msg , 這時(shí)候就沒依賴,所以源碼中這里還有復(fù)雜判斷),setter 的時(shí)候我們會將收集的依賴項(xiàng)觸發(fā) , 從而更新數(shù)據(jù),理解了這些,就能初步理解 Vue 的響應(yīng)式原理 。
相關(guān)經(jīng)驗(yàn)推薦
- 微粒貸提前還款后馬上能借么 微粒貸提前還款之后多久能再貸
- 魂之獵手圖標(biāo)怎么點(diǎn)亮?點(diǎn)亮魂之獵手圖標(biāo)還送iPhone6
- vue 項(xiàng)目build錯(cuò)誤異常的解決方法
- 關(guān)于vue-cli 3配置打包優(yōu)化要點(diǎn)
- oppor17pro中將亮度自動調(diào)節(jié)關(guān)掉具體操作步驟
- QQ閃照怎么保存 怎么保存到手機(jī)
- 明日之后退出營地基礎(chǔ)操作
- 如何導(dǎo)出qq聊天記錄 qq聊天記錄怎么導(dǎo)入
- 知鳥app中將收藏刪掉具體流程介紹
- IE瀏覽器如何恢復(fù)之前意外關(guān)閉的網(wǎng)頁
