使用Vue的小夥伴,都不免會遇到這麼一個「坑」vue
爲何我更新了數據,視圖卻沒法渲染?vue-cli
舉個例子api
<!--使用vue-cli腳手架-->
<template>
<div>
<button @click="change">改變對象屬性</button>
<button @click="add">添加對象屬性</button>
<div v-for="(val, key) in obj" :key="key">
{{ val }}
</div>
</div>
</template>
<script>
export default {
data () {
return {
obj: {
a: '1',
b: '2',
c: '3'
}
}
},
methods: {
change () {
this.obj.a = '0'
},
add () {
this.obj.d = '4'
}
}
}
</script>
複製代碼
當我點擊了 「改變對象屬性」 按鈕,視圖發生了更新數組
而我點擊了 「添加對象屬性」 按鈕,視圖而不更新markdown
而在打開 vue-devtools 工具工具
咱們能夠發現其實數據是已經更新了!oop
只是視圖沒有更新! ui
那這是爲何呢?this
其實這就是涉及的Vue的響應式原理spa
在Vue2.x中,響應式的核心api是 Object.defineProperty
下面用一段js代碼,模擬一下vue響應式源碼
// 定義空對象
const propsObj = {}
// 定義監聽對象
const obj = {
a: '1',
b: '2',
c: '3'
}
// 模擬更新視圖
const updateView = (key, oldVal, newVal) => {
console.log(`視圖更新了~`)
console.log(`key值${key}更新,從 ${oldVal} 更新到 ${newVal} `)
}
// 定義監聽響應式方法
const defineReactive = (target, key, value) => {
// 核心api
Object.defineProperty(target, key, {
get() {
return value
},
set(newValue) {
updateView(key, value, newValue)
value = newValue
}
})
}
// 遍歷設置監聽
for (const key of Object.keys(obj)) {
defineReactive(propsObj, key, obj[key])
}
// 更新數據
propsObj.a = '0' // 觸發響應式
propsObj.d = '4' // 添加對象屬性,不觸發響應式
delete propsObj.a // 刪除對象屬性,不觸發響應式
複製代碼
輸出結果:
可見
若是想要觸發響應式(視圖)更新
你要確保對象的屬性觸發響應式
而在第一個例子中,點擊了 「添加對象屬性」 按鈕,視圖而不更新
由於他在對象作了一個添加屬性的操做
固然!還要注意的是,刪除操做也是沒法觸發響應式的!
總結一下:
「1.確保改變對象屬性是對象已經有的屬性,才能監聽到響應式」
「2.作添加、刪除操做對象屬性,響應式沒法監聽到」
其實,在Vue官網有明確說明
數組也是大同小異的
數組的「小異」在於:
Vue重寫了這些數組方法,讓這些方法能夠觸發視圖更新
使用Vue專門解決此類問題的api
<!--使用vue-cli腳手架-->
<template>
<div>
<button @click="change">改變對象屬性</button>
<button @click="add">添加對象屬性</button>
<div v-for="(val, key) in obj" :key="key">
{{ val }}
</div>
</div>
</template>
<script>
export default {
data () {
return {
obj: {
a: '1',
b: '2',
c: '3'
}
}
},
methods: {
change () {
this.obj.a = '0'
},
add () {
// 添加對象方法
this.$set(this.obj, 'd', '4')
}
}
}
</script>
複製代碼
固然刪除也是相似的,再也不贅述
不過要注意Vue版本: 2.20+
使用強制更新視圖
<!--使用vue-cli腳手架-->
<template>
<div>
<button @click="change">改變對象屬性</button>
<button @click="add">添加對象屬性</button>
<div v-for="(val, key) in obj" :key="key">
{{ val }}
</div>
</div>
</template>
<script>
export default {
data () {
return {
obj: {
a: '1',
b: '2',
c: '3'
}
}
},
methods: {
change () {
this.obj.a = '0'
},
add () {
this.obj.d = '4'
// 對視圖進行強制更新
this.$forceUpdate()
}
}
}
</script>
複製代碼
對於對象,深拷貝能夠使用 JSON.parse(JSON.stringify(obj))
只要改變對象不是同一個對象便可
由於要從新賦值,不該該爲同一對象
這樣才能觸發響應式
舉個例子
<!--使用vue-cli腳手架-->
<template>
<div>
<button @click="change">改變對象屬性</button>
<button @click="add">添加對象屬性</button>
<div v-for="(val, key) in obj" :key="key">
{{ val }}
</div>
</div>
</template>
<script>
export default {
data () {
return {
obj: {
a: '1',
b: '2',
c: '3'
}
}
},
methods: {
change () {
this.obj.a = '0'
},
add () {
// 不可用 const obj = this.obj
// 緣由: 他們是恆等的 指向同一個對象
// 即 conso;e.log(obj === this.obj) // true
// 使用序列、反序列化進行對象深拷貝,對其從新賦值
const obj = JSON.parse(JSON.stringify(this.obj))
obj.d = '4'
this.obj = obj
}
}
}
</script>
複製代碼
而對於數組,能夠使用slice、concat等方法
感謝閱讀