再聊一下用Proxy實現雙向數據綁定(含數組)

上一篇的末尾,筆者簡單介紹了Object.defineProperty在數組監控方面的不足以及其替代品Proxy。可是對於前者總感受還少點什麼,emmmmm...好像是demo,因而筆者精心準備了一下。因此本篇會主要會分紅兩大塊:一是講述如何彌補Object.defineProperty先天不足的狀況下實現對數組的精準監控,二是着重用Proxy實現雙向數據綁定。node

Object.defineProperty

首先呢,對於 Object.defineProperty在數組監控方面的不足,咱們不只要知其然,更要知其因此然。所以先用栗子來證明下這個 不足。上代碼:
let data = {
    list: []
}

Object.keys(data).forEach(function (key) {
    let value = data[key];
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get() {
            return value;
        },
        set(newValue) {
            console.log(`Setting`);
            value = newValue;
            return true;
        }
    })
})

data.list.push(1);                 //--->  A
// data.list = [1, 2, 3];                //--->  B
console.log(data.list);
事先咱們須要明確下:因此對數組的監控,就是能監測到數組元素的 增長或者刪除。所以按照‘理論’,上述代碼若是向 list 中增長元素的時候,理應會有打印 Setting(筆者已經標記了最後的 A行 和 B行)

在只註釋 B行狀況下看運行結果:git

/usr/local/bin/node --inspect-brk=17809 demo.js 
Debugger listening on ws://127.0.0.1:17809/48911764-8533-4c95-a501-20384c924f6a
Debugger attached.
Array(1) [1]

能夠看出,雖然咱們成功得向數組裏增長了一個元素,可是並無打印出 Setting,所以就是說並無監測到數組的變化.es6

在只註釋 A行狀況下看運行結果:segmentfault

/usr/local/bin/node --inspect-brk=27303 demo.js 
Debugger listening on ws://127.0.0.1:27303/2fac4f06-e775-4485-b70b-b2660a98c2b8
Debugger attached.
Setting
Array(3) [1, 2, 3]

咱們成功得向數組裏增長了一個元素也沒有打印出 Setting,說明數組的變化被監測到了.源碼數組

因此咱們能夠大膽得猜想:當監控 數組 數據對象的時候,實質上就是監控數組的 地址,地址不變也就不會被監測到,因此咱們向 listpush 元素的時候並無觸發打印;當咱們直接替換 list 對象的時候就觸發了打印。因此這就是 Object.defineProperty在數組監控方面的不足。

下面就用例子介紹下如何彌補這方面的不足(固然,也是Vue的處理方式)app

其核心思想就是 覆寫 數組對象中的方法,在調用數組方法的同時能觸發回調。下面是核心代碼:
let arrayMethod = Object.create(Array.prototype);
    ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
        Object.defineProperty(arrayMethod, method, {
            enumerable: true,
            configurable: true,
            value: function () {
                let args = [...arguments]
                Array.prototype[method].apply(this, args);
                console.log(`operation: ${method}`)
                dep.notify();
            }
        })
    });
    
[Array Object].__proto__ = arrayMethod;
同時這樣作的好處是,僅僅是數據中的數組對象的原型被修改掉了,並不會影響到全局的數組對象。

而後筆者也作了一個例子,以下:框架

clipboard.png

經過向 list 中添加隨機數或者刪除隨機數,能同步渲染到頁面上,源碼在這函數

Proxy

Proxy 意爲 代理,通俗來講就是在 目標數據對象 外圍設置一層 攔截
舉個形象的栗子:有一個 倉庫,一開始你們能夠任意得存放貨物,後來老闆請了一個 倉庫管家,全部人想要存放貨物的必需要通過管家的手。並且老闆還給管家制定了一套 管理標準,管家對倉庫的管理必須嚴格按照這套標準。

根據這個栗子,羅列了三個關鍵詞:倉庫倉庫管家管理標準,下面用一段簡單的代碼來表述下, 若是想全面得學習Proxy, 請參考阮一峯老師的博客學習

let dataProxy = new Proxy(data, {
    set() {

    },
    get() {

    }
})

那麼代碼與栗子的對於關係就出來了:this

倉庫 --- data
倉庫管家 --- Proxy / dataProxy
管理標準 --- {set(),get()}

並且能夠注意到 Proxy 實際上是個構造函數,全部咱們對原數據對象的操做都得經過構造函數new出來的對象 dataProxy就好像全部貨物的存取都得經過管家同樣

而後接下來筆者用 Proxy 寫了一個栗子,用來 重現 上面對數組的監控,核心代碼以下:

//數據源
let vm = {
    list: [1, 2, 3, 4]
}

let vmProxy = new Proxy(vm.list, {
        set(target, prop, value) {
            console.log(`Setting: ${value}`);
            Reflect.set(target, prop, value);
            dep.notify();
            return true;
        }
    })

咱們能夠注意到咱們並無直接將 vm 用Proxy進行「包裝」,而是將vm.list進行「包裝」。由於筆者在學習的時候發現若是直接用 vm 的話,當咱們向 list 中添加元素的時候並不會被監測到,筆者猜想的緣由和 Object.defineProperty 同樣,栗子呈上。若是有知道緣由的小夥伴,請多多指教。
篇幅有限,這個例子的代碼直接上連接了,有興趣的小夥伴能夠直接down下來看。

數組說完了,剩下就普通類型的數據就是按照正常套路走就對了。另外,筆者此次吸收上篇博客的教訓,將代碼寫得更詳盡,實現方式更貼近框架級別。用Proxy實現數據的雙向綁定,栗子的功能和上篇博客是同樣的,只是內容更加飽滿。該栗子中筆者簡單實現了一個簡單版的類Vue的框架 View,包含Html文檔的解析、Watcher的構造、數據的綁定等,代碼簡單不復雜,因此這裏就不贅述了,我以爲千言萬語不敵一段代碼,那麼相關的代碼就此呈上

感謝小夥伴的捧場!我會堅持寫博客,分享本身的見聞和工做中遇到的坑,共同窗習,共同進步

相關文章
相關標籤/搜索