Vue双向绑定是怎么实现的?
用了Vue長達2年,如果自己去實現一個雙向綁定,我可能一個字母都寫不出來,是時候探究一下了。
先看data里某對象的輸出
data() {return {pagination: {layout: 'prev,pager,next,jumper,sizes,->, total',pageSizes: [10, 20, 30, 40],currentPage: 1,pageSize: 10,total: 0}}},我們可以看到對象屬性都有有兩個相對應的get和set方法,為什么會多出這兩個方法呢?因為vue是通過Object.defineProperty()來實現數據劫持的。
問題來了,什么是Object.defineProperty()
Object.defineProperty()?方法會直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,并返回此對象。注意:此方法是ES6新增的屬性,而IE8不支持ES6語法 所以vue2.0不支持IE8瀏覽器。
關于es6新增的Object.defineProperty()方法是如何使用,可見
ES6-強大的對象方法 Object.defineProperty() - 簡書
以下是本人根據別人的實例敲出來的實例子,自己動下手,加深下印象。
<script>let obj = {id: 1,pname: '小米',price: 1222} function displayDate(){Object.defineProperty(obj, 'price', {value: 1999})console.log(obj);Object.defineProperty(obj, 'count', {value: 100})console.log(obj) Object.defineProperty(obj, 'id', {// writable修改后值是否可以被重寫 ?默認是falsewritable: false})obj.id = 3 //重寫無效console.log(obj);Object.defineProperty(obj, 'address', {value: '山東',// enumerable參數,設置是否可以被遍歷 默認是false,通過該方法添加的默認都是false不會被遍歷enumerable: false,// configurable參數,默認為false。不允許刪除或修改configurable: true //值為false時,即為不可刪除或修改}) console.log(obj)console.log(Object.keys(obj)); //取對象屬性的方法,即遍歷,因為address設置了不可被遍歷delete obj.addressconsole.log(obj);} </script>結果如下
自己動手,印象果然一下就深刻了。
再來一個示例
<script>let obj = {id: 1,pname: '小米',price: 1222} function displayDate(){Object.defineProperty(obj,'pname',{get:function(){//當讀取obj的pname的屬性值的時候,會觸發這個函數console.log('get方法被調用了');//return 的值,就是屬性值return pname;},set:function(val){//set可以接收一個參數,就是你想賦的值//當設置obj的pname的屬性值的時候,會觸發這個函數console.log('set方法被調用了');//可以通過下面方法賦值,text變量就是obj的text的屬性值pname = val;} }) obj.pname = '華為'; console.log(obj.pname)} </script>?
寫到這里,聰明的你應該是知道如何實現VUE的雙向綁定了,是的,它就是利用了2015年ES6標準的Object.defineProperty()方法。
手動實現一個VUE雙向綁定,思路:用Object.defineProperty()的set值向input填充,用input的oninput事件觸發set事件。代碼如下。
var model = {}; Object.defineProperty(model,'txt',{get:function(){},set:function(val){var span = document.getElementsByTagName('span')[0];span.innerHTML = val;} }) var input = document.getElementsByTagName('input')[0];input.oninput = function(){model.txt = input.value;//必然觸發set函數}至此,簡易版的VUE雙向綁定如何實現的大概思路就出來了,但我們的尤雨溪大神,肯定不會這樣寫。一個流行框架的誕生,所需技術與思想,不是我們常人所能達到的。
雙向綁定的完整實現思路是:
我們已經知道實現數據的雙向綁定,首先要對數據進行劫持監聽,所以我們需要設置一個監聽器Observer,用來監聽所有屬性。如果屬性發上變化了,就需要告訴訂閱者Watcher看是否需要更新。因為訂閱者是有很多個,所以我們需要有一個消息訂閱器Dep來專門收集這些訂閱者,然后在監聽器Observer和訂閱者Watcher之間進行統一管理的。接著,我們還需要有一個指令解析器Compile,對每個節點元素進行掃描和解析,將相關指令對應初始化成一個訂閱者Watcher,并替換模板數據或者綁定相應的函數,此時當訂閱者Watcher接收到相應屬性的變化,就會執行對應的更新函數,從而更新視圖。
1.實現一個監聽器Observer,用來劫持并監聽所有屬性,如果有變動的,就通知訂閱者。
2.實現一個訂閱者Watcher,可以收到屬性的變化通知并執行相應的函數,從而更新視圖。
3.實現一個解析器Compile,可以掃描和解析每個節點的相關指令,并根據初始化模板數據以及初始化相應的訂閱器。
圖解
1.實現一個Observer
Observer是一個數據監聽器,其實現核心方法就是前文所說的Object.defineProperty( )。如果要對所有屬性都進行監聽的話,那么可以通過遞歸方法遍歷所有屬性值,并對其進行Object.defineProperty( )處理。如下代碼,實現了一個Observer。
<script>function defineReactive(data, key, val) {observe(val); // 遞歸遍歷所有子屬性Object.defineProperty(data, key, {enumerable: true,configurable: true,get: function() {return val;},set: function(newVal) {val = newVal;console.log('屬性' + key + '已經被監聽了,現在值為:“' + newVal.toString() + '”');}}); }function observe(data) {if (!data || typeof data !== 'object') {return;}Object.keys(data).forEach(function(key) {defineReactive(data, key, data[key]);}); };var library = {book1: {name: ''},book2: '' }; observe(library); library.book1.name = 'vue權威指南'; // 屬性name已經被監聽了,現在值為:“vue權威指南” library.book2 = '沒有此書籍'; </script>后繼observer的改造,訂閱者,解析器的實現本人就不研究了,略過就可以了,如有興趣傳送門。
總結:
在new Vue的時候,在 Observer 中通過 Object.defineProperty() 達到數據劫持,代理所有數據的 getter 和 setter 屬性,在每次觸發 setter 的時候,都會通過 Dep 來通知Watcher,Watcher 作為Observer數據監聽器與Compile模板解析器之間的橋梁,當 Observer 監聽到數據發生改變的時候,通過 Updater 來通知 Compile 更新視圖,而 Compile 通過 Watcher 訂閱對應數據,綁定更新函數,通過 Dep 來添加訂閱者,達到雙向綁定。
vue的雙向綁定原理及實現_深海里擱淺的貓的博客-CSDN博客_vue實現雙向數據綁定原理
Vue 是如何實現數據雙向綁定的?_吖旭玲的博客-CSDN博客_vue如何實現數據雙向綁
??????vue2核心對象defineProperty_明哥前端的博客-CSDN博客
ES6:Object.defineProperty方法_anyw_的博客-CSDN博客
ES6-強大的對象方法 Object.defineProperty() - 簡書
總結
以上是生活随笔為你收集整理的Vue双向绑定是怎么实现的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CEMAPI实战攻略(二)——建立与短信
- 下一篇: 全国计算机等级考试二级 Python 语