
image.png
observer用來實(shí)現(xiàn)對(duì)每個(gè)vue中的data中定義的屬性循環(huán)用Object.defineProperty()實(shí)現(xiàn)數(shù)據(jù)劫持,以便利用其中的setter和getter,然后通知訂閱者,訂閱者會(huì)觸發(fā)它的update方法,對(duì)視圖進(jìn)行更新。
我們介紹為什么要訂閱者,在vue中v-model,v-name,{{}}等都可以對(duì)數(shù)據(jù)進(jìn)行顯示,也就是說假如一個(gè)屬性都通過這三個(gè)指令了,那么每當(dāng)這個(gè)屬性改變的時(shí)候,相應(yīng)的這個(gè)三個(gè)指令的html視圖也必須改變,于是vue中就是每當(dāng)有這樣的可能用到雙向綁定的指令,就在一個(gè)Dep中增加一個(gè)訂閱者,其訂閱者只是更新自己的指令對(duì)應(yīng)的數(shù)據(jù),也就是v-model='name'和{{name}}有兩個(gè)對(duì)應(yīng)的訂閱者,各自管理自己的地方。每當(dāng)屬性的set方法觸發(fā),就循環(huán)更新Dep中的訂閱者。
Object.defineProperty(data,key,descriptor)
vue3.0之前的版本實(shí)現(xiàn)數(shù)據(jù)雙向綁定的主要實(shí)現(xiàn)方法:數(shù)據(jù)劫持(監(jiān)控屬性變化)
// 首先創(chuàng)建一個(gè)輸入框和一個(gè)展示數(shù)據(jù)的視圖
<input type="text" id="demo">
<div id="show"></div>
const oInput = document.elementById('#demo')
const oDiv = document.elementById('#show')
// 給定一個(gè)對(duì)象用于存放數(shù)據(jù),并監(jiān)聽輸入事件
const oData = {
value: '初始值'
}
oInput.oninput = function() {
oData.value = this.value
}
// 更新視圖
function update() {
oDiv.innerText = oData.value
}
// defineRective 方法真正對(duì)對(duì)象的屬性進(jìn)行監(jiān)控,使用 Object.defineProperty 給屬性設(shè)置 get 和 set 監(jiān)聽屬性的讀寫,即數(shù)據(jù)劫持。同時(shí)在寫入數(shù)據(jù)的時(shí)候判斷新數(shù)據(jù)對(duì)比原數(shù)據(jù)是否發(fā)生改變,如果沒有發(fā)生改變則不進(jìn)行額外操作,避免性能浪費(fèi)。
//在寫入更新的數(shù)據(jù)后使用 upDate 對(duì)視圖進(jìn)行更新
function defineRective(data,key,value) {
Object.defineProperty(data,key,{
get:function () {
return value
},
set:function(newValue) {
if(newValue === value) return
value = newValue
update()
}
})
}
// Observer 方法用于監(jiān)控對(duì)象中的任意屬性是否發(fā)生改變。首先判斷數(shù)據(jù)是否存在并且是否為對(duì)象,然后使用 Object.keys() 獲取數(shù)據(jù)對(duì)象中的所有屬性,用 forEach 進(jìn)行遍歷為屬性添加defineRective 監(jiān)控
function Observer(data) {
if(!data || typeof data != 'object') return data
Object.keys(data).forEach(key => {
defineRective(data,key,data[key])
})
}
對(duì)深層次的數(shù)據(jù)進(jìn)行監(jiān)控,即數(shù)據(jù)在數(shù)據(jù)對(duì)象內(nèi)的對(duì)象中時(shí),需要適應(yīng)遞歸的方式添加監(jiān)控
// 創(chuàng)建數(shù)據(jù)
const data = {
valueObj: {
value: '初始值',
name: 'Jaraiya'
}
}
// 監(jiān)聽輸入
oInput.oninput = function() {
oData.valueObj.value = this.value
}
// 調(diào)用時(shí)更新視圖
update() {
oDiv.innerText = oData.valueOf.value
}
// 真正利用defineProperty 進(jìn)行對(duì)象監(jiān)控的函數(shù)
function defineRective(data,key,value) {
Observer(value); # 主要是這一步
Object.defineProperty(data,key,{
get:function() {
return value
},
set:function(newValue) {
if(newValue === value) return
value = newValue
update()
}
})
}