- 最近寫代碼碰到了數(shù)據(jù)響應(yīng)式丟失的問題愧旦,剛開始還以為是值綁定錯(cuò)了/代碼邏輯寫錯(cuò)了世剖,結(jié)果是由于數(shù)據(jù)是額外添加導(dǎo)致的,這次就記錄下錯(cuò)誤出現(xiàn)原因笤虫,加深下印象旁瘫。
1. this.$set
1.1 Vue響應(yīng)式的局限性
對(duì)于對(duì)象:Vue 無法檢測(cè)對(duì)象元素的添加或移除。由于 Vue 會(huì)在初始化實(shí)例時(shí)對(duì)對(duì)象元素執(zhí)行 getter/setter 轉(zhuǎn)化琼蚯,所以對(duì)象元素必須在 data 對(duì)象上存在才能讓 Vue 將它轉(zhuǎn)換為響應(yīng)式的
對(duì)于數(shù)組:當(dāng)你利用索引直接設(shè)置一個(gè)數(shù)組值時(shí)
this.items[indexOfItem] = newValue
酬凳,當(dāng)你修改數(shù)組的長(zhǎng)度時(shí)this.items.length = newLength
注:
this.$set()
是Vue.set()
別名
1.2 對(duì)象形式的實(shí)際例子演示
data() {
return {
obj:{name:'zs'}
}
}
// 錯(cuò)誤演示
methods: {
drag(){
this.obj.age = 18
}
}
// 正確演示
// this.$set(要添加數(shù)據(jù)的對(duì)象, key值, 數(shù)據(jù))
methods: {
drag(){
this.$set(this.obj, 'b', 18)
// 或者這樣
this.obj= Object.assign({}, this.obj, { b: 18 })
}
}
Vue 無法檢測(cè)對(duì)象元素的添加或移除,所以這樣也會(huì)丟失響應(yīng)式
data() {
return {
this.stockList:[{name:'zs' ,id:'1'},{{name:'ls' ,id:'2'}],
this.stockArray:[{name:'zs' ,id:'1',maxLimit:'新字段'},{{name:'ls' ,id:'2',maxLimit:'新字段'}]
}
}
// 錯(cuò)誤演示
methods: {
drag(){
this.stockList.map((item) => {
const newData = this.stockArray.find(x=>x.id===item.id)
if (newData) {
item.maxLimit = newData.maxLimit
}
})
}
}
// 正確演示
methods: {
drag(){
this.stockList.map((item) => {
const newData = this.stockArray.find(x=>x.id===item.id)
if (newData) {
this.$set(item,"maxLimit", newData.maxLimit)
}
})
}
}
以上可以看到遭庶,首先數(shù)組this.stockList
與數(shù)組this.stockArray
進(jìn)行了遍歷宁仔,如果符合條件,數(shù)組this.stockArray
就會(huì)把maxLimit
字段傳遞給數(shù)組this.stockList
,看起來是不是很正常峦睡,但this.stockList
里面是沒有這個(gè)maxLimit
的台诗,這條數(shù)據(jù)屬于額外新增字段,Vue并不會(huì)對(duì)其進(jìn)行響應(yīng)式變化赐俗。
1.3 數(shù)組形式的實(shí)際例子演示
data() {
return {
itemList: ['a', 'b', 'c']
}
}
// 錯(cuò)誤示范
this.itemList[3] = 'd' // 不是響應(yīng)性的
this.itemList.length = 2 // 不是響應(yīng)性的
//正確示范
this.$set(this.itemList, 3, 'd' )
this.itemList.splice(刪除位置, 刪除幾個(gè),要添加的元素)) // 如果只傳索引則從索引位置后刪除之后的元素
- 當(dāng)然拉队,可能有時(shí)候往 數(shù)組對(duì)象 里動(dòng)態(tài)添加子數(shù)組,此時(shí)需要面對(duì)的不單單是添加數(shù)據(jù)的問題阻逮,還要指定key值粱快,此時(shí)演示另一個(gè)辦法,不指定位置叔扼,但指定key值事哭。
demo() {
// 演示數(shù)據(jù)
const children = [{ name: "子級(jí)" }, { name: "子級(jí)" }, { name: "子級(jí)" }];
const list = [{ name: "演示" }, { name: "演示" }, { name: "演示" }];
// 循環(huán)添加 // 可根據(jù)條件判斷是否添加
list.forEach((x) => {
this.$set(x, "children", children);
});
console.log(list);
},
-
如果不使用
$set()
則子級(jí)屬于動(dòng)態(tài)添加的,不會(huì)觸發(fā)界面的渲染瓜富。
效果圖
2 . this.$nextTick()
- Vue 在更新 DOM 時(shí)是異步執(zhí)行的鳍咱。只要偵聽到數(shù)據(jù)變化,Vue 將開啟一個(gè)隊(duì)列与柑,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更谤辜。如果同一個(gè) watcher 被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次价捧。為了在數(shù)據(jù)變化之后等待 Vue 完成更新 DOM再更新數(shù)據(jù)丑念,那么就建議使用
this.$nextTick()
2.1 數(shù)據(jù)演示
- 以下為模擬代碼
<template>
<div class="">
<div>{{value}}</div>
<button @click='btn'>點(diǎn)擊</button>
</div>
</template>
<script>
export default {
data () {
return {
value:'原始數(shù)據(jù)',
}
},
methods: {
btn(){
this.value='我更新了?'
console.log(this.$refs.val.innerText);
this.$nextTick(()=>{
this.value='我更新了'
console.log(this.$refs.val.innerText);
})
}
},
}
</script>
以上可以看到,當(dāng)點(diǎn)擊后直接進(jìn)行賦值操縱结蟋,由于Dom還沒進(jìn)行更新脯倚,所以打印出的還是原始數(shù)據(jù),當(dāng)把賦值放在this.$nextTick()
里就不一樣了嵌屎,這樣回調(diào)函數(shù)將在 DOM 更新完成后被調(diào)用推正。