项目场景:
例如:在我写前端项目的时候,后端给我们的一个json对象,并且我已经渲染在页面上了。但是由于我自己的需求,想往返回的对象里面添加一个字段,后来才意识到不是响应式的。如果我们要让这个新字段是响应式的,就要使用到this.$set来注入数据。
当vue的data里边声明或者已经赋值过的对象或者数组(数组里边的值是对象)时,向对象中添加新的属性,如果更新此属性的值,是不会更新视图的。
问题描述
话不多说,上demo:
<!--先看一个例子-->
<template>
<div class="hello">
<div>{{list}}</div>
<button @click="add">age++</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
list: {name:'LL'}
}
},
methods:{
add(){
if(!this.list.age){
this.list.age=18
}else
{
this.list.age++
}
console.log(this.list)
}
}
}
</script>
<style scoped>
</style>
当我们没有使用this.$set
去添加对象属性时,效果图如下:
新属性age确实已经添加,但页面list并未重新渲染到视图层上,页面并没有响应式的渲染age属性
当我们使用了this.$set
来添加对象属性:
add(){
if(!this.list.age){
// this.list.age=18
this.$set(this.list,'age',18)
}else
{
this.list.age++
}
console.log(this.list)
}
我们可以发现这时候添加的数据就变成响应式数据了。
原因分析:
由于 Vue 会在初始化实例时进行双向数据绑定,使用Object.defineProperty()对属性遍历添加 getter/setter 方法,所以属性必须在 data 对象上存在时才能进行上述过程 ,这样才能让它是响应的。如果要给对象添加新的属性,此时新属性没有进行过上述过程,不是响应式的,所以会出想数据变化,页面不变的情况。此时需要用到$set。
向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property
(比如 this.myObject.newProperty = ‘hi’)
Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。例如:
var vm = new Vue({
data:{
a:1
}
})
// `vm.a` 是响应式的
vm.b = 2
// `vm.b` 是非响应式的
解决方案:
使用:
Vue.set( target, propertyName/index, value )
参数:
{Object | Array} target
{string | number} propertyName/index
{any} value
返回值:设置的值
数组:
this.$set(Array, index, newValue)
注意:这里数组使用的是通过索引来修改会替换值。
对象:
this.$set(Object, 'Key', Value)
注意:这里对象是通过键名来进行修改和操作,所以需要用sting格式。
补充:
有时你可能需要为已有对象赋值多个新 property,比如使用 Object.assign() 或 _.extend()。但是,这样添加到对象上的新 property 不会触发更新。在这种情况下,你应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。
// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
更多相关请看官网Vue响应式原理