使用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等方法
感谢阅读