網(wǎng)上有很多關于數(shù)據(jù)的雙向綁定的介紹,但是感覺描述的太過于累贅。使閱讀者感覺太難理解幢妄。這里我將一步一步循循漸進的方式剧董。領導你們寫一個數(shù)據(jù)的雙向綁定的案例幢尚。 ——觀察者模式
數(shù)據(jù)單向綁定M——V或者V——M
數(shù)據(jù)單向綁定之數(shù)據(jù)驅(qū)動視圖即 M——V
<input id="test1">
</input>
<script>
let data = 2;
const oSpan = document.getElementById("test1");
oSpan.value = data;
// 這里改變數(shù)據(jù)通知視圖變化
setInterval(() => {
data += 1;
oSpan.value = data;
}, 1000)
</script>
數(shù)據(jù)單向綁定之視圖驅(qū)動數(shù)據(jù)即 V——M
<input id="dom2"/>
</body>
<script>
let data;
const oDom = document.getElementById("dom2")
// 通過監(jiān)聽 dom變化通知數(shù)據(jù)改變
oDom.addEventListener("input", (ev) => {
data = ev.target.value
console.log(data)
})
</script>
從上面的案例我們得知要想數(shù)據(jù)改變;我們需要獲取變化翅楼,通知目標尉剩。
得到的思想:
1 需要一個觀察者:收集數(shù)據(jù)或者dom的變化
2 需要一個訂閱者:獲取改變并更新數(shù)據(jù)或者dom
數(shù)據(jù)之雙向綁定實現(xiàn) MVVM
根據(jù)上面我們得到的啟示來一步一步實現(xiàn)。
這里你需要了解一下:Object.defineProperty
數(shù)據(jù)雙綁定一 觀察者
案例只是單成對象毅臊,并沒有做for循環(huán)遍歷
<script>
const obj = {
name: 'yx',
age: 18
};
Object.defineProperty(obj, "name", {
enumerable: true,
configurable: true,
get: function (val) {
console.log("這里要添加訂閱者")
},
set: function (val) {
console.log("這里要通知訂閱者")
}
});
// 讀取數(shù)據(jù)的時候 我們要添加訂閱
setTimeout(() => {
console.log(obj.name)
}, 1000)
// 改變數(shù)據(jù)的時候通知
setTimeout(() => {
obj.name = "yxx"
}, 3000)
</script>
數(shù)據(jù)雙綁定二 訂閱者
// 訂閱者通過訂閱器添加
function Watcher() {
setTimeout(() => {
// 這里是為了觸發(fā)defineProperty 添加訂閱者
this.get()
}, 5000)
}
Watcher.prototype = {
// dom更新
update: function () {
console.log("dom要更新")
},
// 促使添加訂閱者
get: function () {
Dep.target = this; // 這樣處理很方便添加訂閱者理茎,需自己體會
const val = obj.name;
console.log(`我要獲取${val}觸發(fā)添加訂閱者`)
}
};
數(shù)據(jù)雙綁定三 訂閱器
把訂閱者和觀察者聯(lián)合起來
// 訂閱器
function Dep() {
this.subs = []
}
Dep.prototype = {
// 添加訂閱者
addSub: function (sub) {
this.subs.push(sub)
},
// 通知訂閱者
notify: function () {
this.subs.forEach(function (sub) {
sub.update();
});
}
};
數(shù)據(jù)雙綁定實例 測試
<script>
const obj = {
name: 'yx',
age: 18
};
// 訂閱器 添加/通知 訂閱者
function Dep() {
this.subs = []
}
Dep.prototype = {
addSub: function (sub) {
this.subs.push(sub)
},
notify: function () {
this.subs.forEach(function (sub) {
sub.update();
});
}
};
dep = new Dep();
function Observer(data, key) {
Object.defineProperty(obj, "name", {
enumerable: true,
configurable: true,
get: function (val) {
console.log("這里要添加訂閱者");
// dep.addSub(objW)
dep.addSub(Dep.target)
},
set: function (val) {
console.log("這里要通知訂閱者")
dep.notify()
}
});
}
observer = new Observer(obj, "name");
function Watcher() {
setTimeout(() => {
this.get()
}, 5000)
}
Watcher.prototype = {
// dom更新
update: function () {
console.log("dom要更新")
},
// 促使添加訂閱者
get: function () {
Dep.target = this;
const val = obj.name;
console.log("我要獲取數(shù)據(jù)觸發(fā)添加訂閱者")
}
};
new Watcher()
setTimeout(() => {
obj.name = "yxxx"
}, 10000)
</script>
若要對多層對象監(jiān)聽則可以對對象實現(xiàn)for循環(huán)對每一個屬性進行監(jiān)聽