當你把一個普通的 JavaScript 對象傳給 Vue 實例的 data 選項,Vue 將遍歷此對象所有的屬性,并使用 Object.defineProperty 把這些屬性全部轉為 getter/setter。Object.defineProperty 是 ES5 中一個無法 shim 的特性,這也就是為什么 Vue 不支持 IE8 以及更低版本瀏覽器
以上摘自 深入響應式原理
那么,把這些屬性全部轉為 getter/setter 具體是怎樣一個過程呢?本文不深入具體,簡單大致了解其過程,旨在整體把握,理解其主要思路
假設代碼如下:
const vm = new Vue({ el: '#app', data: { msg: 'hello world' }})data 選項可以接收一個對象或者方法,這里以對象為例(其實最后都會轉為對象)
首先,這個對象的所有鍵值對都會被掛載在 vm._data 上(此外 vm._data 對象上還有個 __ob__ key,暫時可以忽視),這樣我們便能用 vm._data.msg 訪問到數據
但是通常我們是用 vm.msg 這樣訪問數據,如何做到的呢?其實就是做了個代理,將 data 鍵值對中的 vm[key] 的訪問都代理到 vm._data[key] 上
proxy(vm, `_data`, key)export function proxy (target: Object, sourceKey: string, key: string) { sharedPropertyDefinition.get = function proxyGetter () { return this[sourceKey][key] } sharedPropertyDefinition.set = function proxySetter (val) { this[sourceKey][key] = val } Object.defineProperty(target, key, sharedPropertyDefinition)}通常 vm._data (下劃線變量)用作內部程序,對外暴露的 API 是 vm.$data,其實這兩者也是一個東西,也是做了個代理,代碼大概這樣:
const dataDef = {}dataDef.get = function () { return this._data }Object.defineProperty(Vue.prototype, '$data', dataDef)if (process.env.NODE_ENV !== 'production') { dataDef.set = function () { warn( 'Avoid replacing instance root $data. ' + 'Use nested data properties instead.', this ) }}簡單理解就是訪問 vm.data.msg 其實就是訪問 vm._data.msg。如果直接在開發環境對 vm.data = xxx這樣的賦值,而不是vm.$data.msg = xxx` 這樣的賦值,后者是沒問題的)
至此,我們理解了為什么能用 vm.msg、vm._data.msg 以及 vm.$data.msg 三種方式獲取/改變數據,最原始的數據是 vm._data.msg,而另外兩者即代理了 _data 的數據,vm.$data.msg 即為 Vue 向外提供的 API,一般情況下開發我們直接用 vm.msg 這樣比較多,也方便,如果要獲取整個 data,程序中需要用 this.$data,而不是 this.data
接下來說 getter/setter
將 demo 稍微添點東西:
const vm = new Vue({ el: '#app', data: { msg: 'hello world' }, computed: { msg2() { return this.msg + '123' } }})msg2 是依賴于 msg 的,當 msg 改變的時候,msg2 的值需要自動更新,msg 的改變可以在 vm._data.msg 的 setter 中監聽到,但是怎么知道 msg2 是依賴于 msg 的呢?
新聞熱點
疑難解答
圖片精選