Vue數據代理的實現流程逐步講解

一,前言

上篇,主要介紹瞭 Vue 數據初始化流程中,數組類型的數據劫持是如何實現的,核心思路如下:

出於對性能的考慮,Vue 沒有對數組采用 Object.defineProperty 進行遞歸劫持,而是對能夠導致原數組變化的 7 個方法進行瞭攔截和重寫,實現瞭對數組的數據劫持

至此,已經完成瞭對響應式數據(對象和數組)的劫持(深層劫持)操作

本篇,繼續介紹 Vue 數據初始化流程中, Vue 實例上數據代理的實現

二,數據代理的實現

1,Vue 是如何操作數據的

在 Vue 中,是可以在外部直接通過 vm 實例進行數據訪問和操作:

let vm = new Vue({
  el: '#app',
  data() {
    return { message: 'Hello Vue', obj: { key: "val" }, arr:[1,2,3]}
  }
});
console.log(vm.message)
console.log(vm.arr.push(4))

拋出問題:vm.message 等價於 $options.data.message,是如何實現的?

2,當前是如何操作數據的

當前代碼,外部的 vm 實例隻能拿到 vm. o p t i o n s ,拿到 d a t a 需要 v m . options,拿到 data 需要 vm. options,拿到data需要vm.options.data

// src/state.js#initData
function initData(vm) {
    let data = vm.$options.data;
    data = isFunction(data) ? data.call(vm) : data;
    observe(data);
    data.message
    data.arr.push(4); 
}

要想實現 vm.message 和 $options.data.message 等效

相當於將 vm 實例操作代理到 $options.data 上,即實現數據代理

3,數據代理的思路

為瞭讓外部的 vm 實例能夠拿到觀測後的 data,將處理後的 data 直接掛載到 vm 上

// src/state.js#initData
function initData(vm) {
    let data = vm.$options.data;
    data = vm._data = isFunction(data) ? data.call(vm) : data;
    observe(data);
}

這樣,vm 實例就能夠在外部通過 vm._data.message 獲取到 data.message

接下來,再做一次代理,將 vm 實例操作(vm.message),代理到 vm._data 上即可

4,數據代理的實現

通過 Object.defineProperty 對 _data 中的數據操作進行劫持

即:vm.message 在 vm 實例上取值時,將它代理到 vm._data 上取值

// src/state.js#initData
function initData(vm) {
    let data = vm.$options.data;
    data = vm._data = isFunction(data) ? data.call(vm) : data;
    observe(data);
    // 當 vm.message 在 vm 實例上取值時,將它代理到vm._data上去取
    for(let key in data){
        Proxy(vm, key, '_data')
    }
}
// src/state.js#Proxy
/**
 * 代理方法
 *  當取 vm.key 時,將它代理到 vm._data上去取
 * @param {*} vm        vm 實例
 * @param {*} key       屬性名
 * @param {*} source    代理目標,這裡是vm._data
 */
function Proxy(vm, key, source) {
    Object.defineProperty(vm, key, {
        get(){
            return vm[source][key]
        },
        set(newValue){
            vm[source][key] = newValue;
        }
    })
}

5,數據代理的測試

let vm = new Vue({
  el: '#app',
  data() {
    return { message: 'Hello Vue', obj: { key: "val" }, arr:[1,2,3]}
  }
});
console.log(vm)
console.log(vm.message)

觀察打印結果:

獲取 vm 實例時,會通過 get 方法將 _data 全部屬性打印出來

當前 vm 實例上,包含 data 全部屬性及對應的 get、set 方法

這樣,就實現瞭數據代理:

當從 vm 實例取值時,就會被代理到 vm._data 取值

三,結尾

本篇主要介紹瞭 Vue 數據初始化流程中,Vue 實例上數據代理的實現,核心思路如下:

  • 將 data 暴露在 vm._data 實例屬性上
  • 利用 Object.defineProperty 將 vm.xxx 操作代理到 vm._data 上

到此這篇關於Vue數據代理的實現流程逐步講解的文章就介紹到這瞭,更多相關Vue數據代理內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: