Vue中關于陣列與物件修改觸發頁面更新的機制與原理簡析
相關問題
陣列
使用索引直接賦值與直接修改陣列length時,不會觸發頁面更新,
例如:
<script>
export default {
name: "HomeView",
data: () => ({
list1: ["A", "B"],
}),
methods: {
btnClicked() {
this.list1[0] = "C"
this.list1[2] = "C"
},
},
}
</script>
或是
<script>
export default {
name: "HomeView",
data: () => ({
list1: [{ text: "123" }, { text: "456" }],
}),
methods: {
btnClicked() {
this.list1[0] = { text: "789" }
},
},
}
</script>
頁面并不會觸發更新,
物件
頁面初始化完成后,在方法中直接對data內宣告物件當前不存在的屬性進行賦值來為物件新增屬性時,頁面也不會回應渲染,
例如:
<script>
export default {
name: "HomeView",
data: () => ({
obj1: { a: "a", b: "b" },
}),
methods: {
btnClicked() {
this.obj1.c = "c"
},
},
}
</script>
頁面并不會觸發更新,
原因
Vue在初始化時會將data內所有的屬性嵌套遍歷并重寫其Getter和Setter方法,借此實作回應式屬性,
然而對于在頁面渲染完成后加入data的屬性,Vue并不會將其變為回應式,
一些深入的探究
陣列
Vue對于陣列是僅將其對應下標的物件的屬性變為回應式,而這個下標本身是無法成為回應式的,
data: () => ({
list1: [{ text: "123" }, { text: "456" }],
})
使用如上的data宣告,
方法A:
this.list1[0] = { text: "789" }
方法B:
this.list1[0].text = "789"
方法B可以被正確回應而方法A不可以,
方法A將陣列下標為0的位置替換為了一個新的物件,而因為陣列下標不是回應式的,因此沒有觸發頁面重繪,
同時,由于陣列下標為0的位置替換為了一個新的物件,而這個新的物件并沒有被配置為回應式,那對于這個物件屬性的修改也不會觸發頁面更新,如下:
this.list1[0] = { text: "789" }
this.list1[0].text = "456"
由于新的物件的屬性并沒有被配置為回應式,那么即使對這個物件的屬性進行修改,頁面也不會被更新,
既然下標本身無法成為回應式,不妨嘗試:
<script>
export default {
name: "HomeView",
data: () => ({
list1: ["A", "B"],
}),
methods: {
btnClicked() {
this.list1[0] = "C"
},
},
}
</script>
通過下標修改陣列的對應值也無法觸發視圖更新,
物件
data: () => ({
obj1: { a: { text: "a" }, b: { text: "b" } },
}),
使用如上的data宣告,
this.obj1.a = { text: "c" }
成功觸發視圖更新,
與陣列下標不同,物件的屬性在初始化是被定義為回應式的,因此直接對屬性賦值物件是能夠觸發視圖更新的,不像對陣列的對應下標賦值而不會觸發視圖更新,
解決方案
陣列
1. 內置API
如果需要向陣列加入新的成員,則可以直接使用陣列的push方法,
此外,下列陣列方法也可以自動的觸發視圖重繪:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
2. 將陣列重新賦值,修改參考地址
為陣列新增一個字串成員"C"
this.list1 = this.list1.concat(["C"])
由于list1是data的屬性,list1的參考發生改變,就會觸發視圖更新,
修改陣列的第一個值
let tempList = this.list1.concat([]) // 深拷貝,等價于一個新陣列,使用slice,JSON都可以,
tempList[0] = "666"
this.list1 = tempList
通過原陣列新建一個新陣列,修改新陣列后再將新陣列賦值給原陣列,由于原陣列作為data的屬性,其參考被修改,觸發視圖更新,
3. Vue.$set() 方法
使用Vue.$set可以為data物件添加一個新的回應式屬性,且觸發視圖更新,
定義:
Vue.$set(物件或陣列, 物件屬性名或陣列下標, 值)
向list1物件的0索引位置賦值一個新的回應式物件,同時觸發視圖更新:
Vue.$set(this.list1, 0, { text: "789" })
如果在組件中應使用this.$set來代替:
this.$set(this.list1, 0, { text: "789" })
物件
1. 將物件重新賦值,修改參考地址
思路與陣列的類同,
使用JSON、手寫遞回、lodash深拷貝均可,但如果物件內含方法,則不能使用JSON來完成深拷貝,
深拷貝完成后修改對應屬性后賦值給原物件即可,
2. Vue.$set() 方法
物件同樣可以使用$set() 方法修改,
定義:
Vue.$set(物件或陣列, 物件屬性名或陣列下標, 值)
將obj1物件的a屬性賦值為字串"b"并觸發視圖更新:
Vue.$set(this.obj1, a, "b")
如果在組件內,則應使用:
this.$set(this.obj1, a, "b")
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/539673.html
標籤:其他
