- 一個(gè)物體能對(duì)外界的刺激作出反應(yīng),那他就是響應(yīng)式的
- const vm = new Vue({data:{n:0}})
- 我如果修改 vm.n ,那么UI中的 n 就回來(lái)響應(yīng)我
- Vue通過(guò)Object.defineProperty來(lái)實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式
- Vue通過(guò)Object.defineProperty的 getter/setter 對(duì)收集的依賴項(xiàng)進(jìn)行監(jiān)聽(tīng),在屬性被訪問(wèn)和修改時(shí)通知變化,進(jìn)而更新視圖數(shù)據(jù)
getter/setter
用于對(duì)屬性的讀寫(xiě)進(jìn)行監(jiān)控
getter
let obj1 = {
姓: "高",
名: "圓圓",
姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log( obj1.姓名());
// 高圓圓
let obj2 = {
姓: "高",
名: "圓圓",
get 姓名() {
return this.姓 + this.名;
},
age: 18
};
console.log( obj2.姓名);
// 高圓圓
- 對(duì)比代碼,發(fā)現(xiàn)只是在姓名函數(shù)處添加了 get ,最后使用的時(shí)候就不再需要加 ()
- getter 就是這樣用的,不加括號(hào)的函數(shù)
setter
let obj3 = {
姓: "高",
名: "圓圓",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xxx){
this.姓 = xxx[0]
this.名 = xxx.slice(1)
},
age: 18
};
obj3.姓名 = '劉詩(shī)詩(shī)'
console.log(`姓 ${obj3.姓}睛低,名 ${obj3.名}`)
// 姓 劉,名 詩(shī)詩(shī)
- setter 就是這樣用的蹬敲,用 = xxx 觸發(fā) set 函數(shù)
Object.defineProperty
用于給對(duì)象添加新屬性暇昂,也可以給對(duì)象添加 getter/setter
let obj = {}
Object.defineProperty(obj, 'x', {value: 1})
Object.defineProperty(obj, 'y', {
get(){...}
set(value){...}
})
監(jiān)聽(tīng)與代理
- 對(duì) myData 對(duì)象的屬性讀寫(xiě),全權(quán)由另一個(gè)對(duì)象 vm 負(fù)責(zé)
- 那么 vm 就是 myData 的代理 (類似房東租房通過(guò)中介)
- 比如 myData.n 不用伴嗡,偏要用 vm.n 來(lái)操作 myData.n
代碼案例:bold-heyrovsky-8jcm2 - CodeSandbox
vm = new Vue({data: myData}) 就做了和上面代碼類似的事情急波。
- vm 成為了 myData 的代理
- 會(huì)對(duì) mydata 的所有屬性進(jìn)行監(jiān)控
目的: 無(wú)論直接修改 myData.n 還是修改 vm.n,vm 都會(huì)收到通知瘪校,然后調(diào)用觸發(fā)重新渲染
Vue 對(duì) methods 和 computed 也有類似處理澄暮。
Object.defineProperty 的問(wèn)題
Object.defineProperty(obj, 'n', {...}
必須有 'n' 才可以監(jiān)聽(tīng)&代理 obj.n
//無(wú) data.n
new Vue({
data: {},
template: `
<div>{{n}}</div>
`
}).$mount("#app");
無(wú) data.n 或 data.n 的值為 undefined ,此時(shí) n 被引用阱扬,不會(huì)有顯示朴沿,控制臺(tái)會(huì)報(bào)警告
//有 data.obj餐胀,但無(wú) data.obj.n
new Vue({
data: {
obj: {
a: 0 // obj.a 會(huì)被 Vue 監(jiān)聽(tīng) & 代理
}
},
template: `
<div>
{{obj.b}}
<button @click="setB">set b</button>
</div>
`,
methods: {
setB() {
this.obj.b = 1; //不顯示1
}
}
}).$mount("#app");
data.obj 中開(kāi)始并沒(méi)有 b痢甘,后面運(yùn)行 this.obj.b = 1 并不會(huì)讓 1 出現(xiàn)在頁(yè)面中宪拥,因?yàn)?vm 沒(méi)有監(jiān)聽(tīng)&代理 data.obj.b
解決辦法:
- 在開(kāi)始就定義好 data.obj.n = undefined
- 使用 Vue.set 或者 this.$set
一個(gè)特例:
//html
<div id="app">
<span class=span-a>
{{obj.a}}
</span>
<span class=span-b>
{{obj.b}}
</span>
</div>
js
//js
var app = new Vue({
el: '#app',
data: {
obj: {
a: 'a',
}
},
})
app.obj.a = 'a2'
app.obj.b = 'b'
最終 span-a 中會(huì)顯示 a2,span-b 中顯示 b
這是因?yàn)橐晥D更新是異步的窃蹋,a1 變成 a2 時(shí)卡啰,Vue 監(jiān)聽(tīng)到這個(gè)變化静稻,并不會(huì)馬上更新視圖,而是創(chuàng)建一個(gè)視圖更新任務(wù)到任務(wù)隊(duì)列里匈辱。然后繼續(xù)運(yùn)行代碼 app.obj.b = 'b'振湾。視圖更新時(shí),Vue 會(huì)去做 diff亡脸,發(fā)現(xiàn) a 和 b 都變了押搪,于是去更新 span-a 和 span-b。
Vue.set 和 this.$set
作用:
- 新增key
- 自動(dòng)創(chuàng)建代理和監(jiān)聽(tīng)(如果沒(méi)有創(chuàng)建過(guò))
- 觸發(fā)UI更新(不會(huì)立即更新)
this.$set(this.object,'m',100)
變異方法
在數(shù)組中浅碾,數(shù)組的長(zhǎng)度一直增加大州,下標(biāo)就是key,沒(méi)辦法提前聲明所有下標(biāo)垂谢,難道每次改數(shù)組都要Vue.set 或者 this.$set摧茴?
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
this.array[3] = "d"; //不顯示d
}
}
}).$mount("#app");
使用變異方法
push()、pop()埂陆、shift()、unshift()娃豹、splice()焚虱、sort()、reverse()
new Vue({
data: {
array: ["a", "b", "c"]
},
template: `
<div>
{{array}}
<button @click="setD">set d</button>
</div>
`,
methods: {
setD() {
this.array.push('d'); //修改后的push可完成我們想要的操作
}
}
}).$mount("#app");