用過vue的人都知道 vue2.x實現雙向數據綁定的原理是利用了 Object.defineProperty() 這個方法重新定義了對象獲取屬性值(get)和設置屬性值(set)的操作來實現的。
在MDN上對該方法的說明是:Object.defineProperty() 方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性, 並返回這個對象。
它接收三個參數,要操作的對象,要定義或修改的對象屬性名,屬性描述符。重點就是最后的屬性描述符。
屬性描述符是一個對象,主要有兩種形式:數據描述符和存取描述符。
這兩種對象只能選擇一種使用,不能混合兩種描述符的屬性同時使用。上面說的get和s et就是屬於存取描述符對象的屬性。
然后我們可以通過在存取描述符中的get和set方法內寫入自定義的邏輯來實現對象獲取屬性和設置屬性時的行為。
用代碼演示vue2.x的原理
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div> <input type="text" id="input"> <span id="text"></span> </div> </body> </html> <script> var obj = {}; Object.defineProperty(obj, 'prop', { get: function () { return val; }, set: function (newVal) { val = newVal; document.getElementById('text').innerHTML = val; } }); document.addEventListener('keyup', function (e) { obj.prop = e.target.value; }); </script>
瀏覽器展示
Vue3.x是用ES6的語法 Proxy對象來實現的,這個玩意兒也可以實現數據的劫持
用代碼演示vue3.x的原理
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div> <input type="text" id="input"> <span id="text"></span> </div> </body> </html> <script> var obj = {}; var obj1 = new Proxy(obj, { // target就是第一個參數obj, receive就是返回的obj(返回的proxy對象) get: function (target, key, receive) { // 返回該屬性值 return target[key]; }, set: function (target, key, newVal, receive) { // 執行賦值操作 target[key] = newVal; document.getElementById('text').innerHTML = target[key]; } }) document.addEventListener('keyup', function (e) { obj1[0] = e.target.value; }); </script>
瀏覽器展示為
相比於vue2.x,使用proxy的優勢如下
- 1 defineProperty只能監聽某個屬性,不能對全對象監聽
- 2 可以省去for in、閉包等內容來提升效率(直接綁定整個對象即可)
- 3 可以監聽數組,不用再去單獨的對數組做特異性操作
- vue3.x可以檢測到數組內部數據的變化