蹭波熱度--一步步實現 vue3 的基於Proxy的MVVM

vue3的mvvm實現原理是基於Proxy實現的,可比vue2的Object.defineProperty簡明扼要不少,也很巧妙,demo不難,一步步實現吧,總共就一個js文件,不超過100行代碼,要什麼自行車。vue

1 勾勒出測試框架,大致分爲幾個部分 :

  1. 更新視圖的方法react

  2. 把數據變爲響應式數據的方法數組

  3. 測試數據緩存

  4. 改變測試數據 觸發更新視圖方法框架

    // 用最簡化的模型來模式vue3的mvvm實現原理
    
    // 用這個方法來模式視圖更新
    function updateView() {
        console.log('觸發視圖更新啦')
    }
    // 把原目標對象 轉變 爲響應式的對象
    function reactive(target) {
        // todo 具體如何轉變,以及綁定視圖更新方法
        let proxyed = new Proxy(target, options)
        return proxyed
    }
    
    // 測試數據
    let obj = {
        name: 'Ace7523',
        array: ['a', 'b', 'c']
    }
    
    // 把原數據轉變響應式的數據
    let reactivedObj = reactive(obj)
    
    // 改變數據,指望會觸發updateView() 方法 從而更新視圖
    reactivedObj.name = 'change'
    複製代碼

小結 準備工做算是作完了,接下來就是實現reactive方法,而後修改一下obj對象,看是否會觸發updateView()方法mvvm

2 實現把原數據轉變爲響應式數據的reactive方法

  1. 利用 new Proxy(target, options) 對target對象,進行數據改造測試

  2. 完善 optionsspa

    // 用最簡化的模型來模式vue3的mvvm實現原理
     
     // 用這個方法來模式視圖更新
     function updateView() {
         console.log('觸發視圖更新啦')
     }
     // 把原目標對象 轉變 爲響應式的對象
     const options = {
         set(target, key, value, reciver) {
             updateView()
             return Reflect.set(target, key, value, reciver)
         },
         get(target, key, reciver) {
            return Reflect.get(target, key, reciver)
         },
         deleteProperty(target, key) {
             return Reflect.deleteProperty(target, key)
         }
     }
     function reactive(target) {
         // todo 具體如何轉變,以及綁定視圖更新方法
         let proxyed = new Proxy(target, options)
         return proxyed
     }
    
     // 測試數據
     let obj = {
         name: 'Ace7523',
         array: ['a', 'b', 'c']
     }
    
     // 把原數據轉變響應式的數據
     let reactivedObj = reactive(obj)
    
     // 改變數據,指望會觸發updateView() 方法 從而更新視圖
     reactivedObj.name = 'change'
    複製代碼

小結 其實本質上就是不用Object.defineProperty 而改用別的方式來重寫對象的get set方法了,也就是Proxy 的方式代理

3 繼續完善reactive方法, 不就是遞歸嘛,拿去

  1. 利用遞歸 對測試數據深層次代理
  2. 例如 本測試數據中 obj.array也是對象,因此在獲取obj.array的時候,判斷是不是對象,若是是的話,對這個對象也執行reactive方法,讓這對象也成爲響應式對象,以此類推,就能夠把測試數據所有轉變爲響應式對象

添加的代碼以下isObject()方法判斷是不是對象,修改options.get()code

function isObject(t) {
    return typeof t === 'object' && t !== null
}
// 把原目標對象 轉變 爲響應式的對象
const options = {
    set(target, key, value, reciver) {
        updateView()
        return Reflect.set(target, key, value, reciver)
    },
    get(target, key, reciver) {
        const res = Reflect.get(target, key, reciver)
        if(isObject(target[key])){
            return reactive(res)
        }
        return res
    },
    deleteProperty(target, key) {
        return Reflect.deleteProperty(target, key)
    }
}
複製代碼

小結 這裏比較巧妙,建議對比下vue2中的Object.defineProperty的方法對比着看。

4 繼續完善reactive()方法 && 添加緩存 -- 完整代碼以下

  1. 防止對數組添加數據時候,重複觸發更新視圖的方法

  2. 利用weakMap 記錄原數據是否進行過代理,若是代理過,則返回記錄值,再也不重複代理

    // 用最簡化的模型來模式vue3的mvvm實現原理
    
     // 用這個方法來模式視圖更新
     function updateView() {
         console.log('觸發視圖更新啦')
     }
     function isObject(t) {
         return typeof t === 'object' && t !== null
     }
     // 把原目標對象 轉變 爲響應式的對象
     const options = {
         set(target, key, value, reciver) {
             if(target.hasOwnProperty(key)){
                 updateView()
             }
             return Reflect.set(target, key, value, reciver)
         },
         get(target, key, reciver) {
             const res = Reflect.get(target, key, reciver)
             if(isObject(target[key])){
                 return reactive(res)
             }
             return res
         },
         deleteProperty(target, key) {
             return Reflect.deleteProperty(target, key)
         }
     }
     // 用來作緩存
     const toProxy = new WeakMap()
    
     function reactive(target) {
         if(!isObject(target)){
             return target
         }
         // 若是已經代理過了這個對象,則直接返回代理後的結果便可
         if(toProxy.get(target)){
             return toProxy.get(target)
         }
         let proxyed = new Proxy(target, options)
         toProxy.set(target, proxyed)
         return proxyed
     }
    
     // 測試數據
     let obj = {
         name: 'Ace7523',
         array: ['a', 'b', 'c']
     }
    
     // 把原數據轉變響應式的數據
     let reactivedObj = reactive(obj)
    
     // 改變數據,指望會觸發updateView() 方法 從而更新視圖
     reactivedObj.name = 'change'
    複製代碼

小結 完整版代碼暫時就這麼多,沒有考慮過多的邊界條件,感興趣的朋友能夠複製出來玩玩看,改改測試數據看看會不會觸發更新視圖的方法~~~

相關文章
相關標籤/搜索