簡單易懂的雙向數據綁定解讀

簡單易懂的雙向數據綁定解讀html

數據更新視圖的重點是如何知道數據變了,只要知道數據變了,那麼接下去的事都好處理。如何知道數據變了,就是經過Object.defineProperty( )對屬性設置一個set函數,當數據改變了就會來觸發這個函數,因此咱們只要將一些須要更新的方法放在這裏面就能夠實現data更新view了前端

實現一個Observeres6

Observer是一個數據監聽器,其實現核心方法就是前文所說的Object.defineProperty( )。若是要對全部屬性都進行監聽的話,那麼能夠經過遞歸方法遍歷全部屬性值,並對其進行Object.defineProperty( )處理。以下代碼,實現了一個Observer。瀏覽器

Object有一個名爲defineProperty的方法,能夠設置訪問器屬性,好比:dom

var obj = {}
Object.defineProperty(obj, 'a', {
    get: function(){
      console.log('this is the getter')
      return 1
    },
    set: function(){
      console.log('change new value')
    }
})
// 獲取對象的值
obj.a
// console.log(obj)
// 改變對象a的值
//obj.a = 1
複製代碼

當咱們執行obj.a的時候會觸發get函數,控制檯會打印'this is the getter',當咱們爲obj.a賦值的時候,obj.a=2;這是控制檯會打印"change new value"你們能夠把getter和setter理解成獲取對象屬性值和給對象屬性賦值時的鉤子就能夠了。函數

雙向數據綁定實現思路this

咱們已經知道實現數據的雙向綁定,首先要對數據進行劫持監聽,因此咱們須要設置一個監聽器Observer,用來監聽全部屬性。若是屬性發上變化了,就須要告訴訂閱者Watcher看是否須要更新。由於訂閱者是有不少個,因此咱們須要有一個消息訂閱器Dep來專門收集這些訂閱者,而後在監聽器Observer和訂閱者Watcher之間進行統一管理的。接着,咱們還須要有一個指令解析器Compile,對每一個節點元素進行掃描和解析,將相關指令對應初始化成一個訂閱者Watcher,並替換模板數據或者綁定相應的函數,此時當訂閱者Watcher接收到相應屬性的變化,就會執行對應的更新函數,從而更新視圖。所以接下去咱們執行如下3個步驟,實現數據的雙向綁定:spa

1.實現一個監聽器Observer,用來劫持並監聽全部屬性,若是有變更的,就通知訂閱者。prototype

2.實現一個訂閱者Watcher,能夠收到屬性的變化通知並執行相應的函數,從而更新視圖。雙向綁定

3.實現一個解析器Compile,能夠掃描和解析每一個節點的相關指令,並根據初始化模板數據以及初始化相應的訂閱器。

流程圖以下:

訂閱者模式:訂閱者模式也叫「訂閱-發佈者模式」,對於前端來講這種模式簡直無處不在,好比咱們經常使用的xx.addEventListener('click',cb,false)就是一個訂閱者,它訂閱了click事件,當在頁面觸發時,瀏覽器會做爲發佈者告訴你,能夠執行click的回調函數cb了。

eg: https://jsbin.com/biyulaz/edit?html,output

實現一個Watcher

好比,士兵與長官就是一個訂閱與發佈者的關係,士兵的全部行動都經過長官來發布,只有長官發號施令,士兵們才能執行對應的行動。

// obj是一個發佈者, 發佈信息時調用dep的notify方法
obj = {
    pub: function(dep){
        dep.notify()
    }
}

//Dep是連接訂閱者和發佈者的一個橋樑, 訂閱者將本身存入subs中, 發佈者經過pub方法來通知subs中的每個訂閱者
function Dep(){
    this.subs = []
}
Dep.prototype.notify = function () {
    // 通知訂閱者, 並讓它們執行update方法
    this.subs.forEach(item => {
        item.update()
    })
}

// 訂閱者
function Watcher(msg) {
    this.msg = msg
}
Watcher.prototype.update = function () {
    console.log('dom' + this.msg + '更新');
}

// 建立三個訂閱者
var a = new Watcher(1),
    b = new Watcher(2),
    c = new Watcher(3);

var dep = new Dep();

// 將三個訂閱者push進Dep.subs中
dep.subs.push(a)
dep.subs.push(b)
dep.subs.push(c)

// 發佈者發佈
obj.pub(dep)
複製代碼

在這段代碼中,obj是發佈者,Watcher實例是訂閱者,Dep用來儲存訂閱者,以及接受發佈者通知的一個媒介

proxy(es6)實現

var nameProxy = new Proxy({
			name: 'gaodeng'
		}, {
			get: function (target, key, value) {
				console.log('觸發getter鉤子函數, 值爲: ', value)
				return target[key];
			},
			set: function (target, key, value) {
				console.log('觸發setter鉤子函數, 值爲: ', value)
				target[key] = value;
			}
		});
// 獲取		
nameProxy.name
// 設置
nameProxy.name = '小明'
複製代碼

最後一個完整的栗子: https://jsbin.com/yijorunepe/edit?html,output

相關文章
相關標籤/搜索