一、添加属性的问题

<template>
  <div>
	<p v-for="(value,key) in item" :key="key">
	    {{ value }}
	</p>
	<button @click="addProperty">动态添加新属性</button>
  </div>
</template>


<script>
export default {
 data:()=>{
        item:{
            oldProperty:"旧值"
        }
    },
    methods:{
        addProperty(){
            this.item.newProperty = "新值"  // 为items添加新属性
            console.log(this.items)  // 输出带有newProperty的items
        }
    }

};
</script>

这时候我们会遇到一个问题:在方法中修改变量时,log打印结果了,但是dom并没有重新渲染。

二、问题分析:

vue2是用过Object.defineProperty实现数据响应式,组件初始化才递归遍历item,这时候并没有给item的每个属性添加setget方法,所有后来并没有给newVal设置成响应式数据,结果就是修改后不会视图更新。

const item = {}
Object.defineProperty(obj, 'oldProperty', {
        get() {
            console.log(`get oldProperty:${val}`);
            return val
        },
        set(newVal) {
            if (newVal !== val) {
                console.log(`set oldProperty:${newVal}`);
                val = newVal
            }
        }
    })
}

三、解决方案 

由于Vue已经不允许在已经创建的实例上添加新的响应式属性,所有可以采用以下三种方法:

  • Vue.set()

  • Object.assign()

  • $forcecUpdated()

(1)、Vue.set()

Vue.set( target, propertyName/index, value )

  • 参数1:target:要修改的对象或数组
  • 参数2:propertyName/index:属性或下标
  • 参数3:value:修改后的value值

这里再次调用defineReactive方法,给新增属性设置成响应式,内部还是通过Vue.defineProperty实现属性拦截。

function set (target: Array<any> | Object, key: any, val: any): any {
  ...
  defineReactive(ob.value, key, val)
  ob.dep.notify()
  return val
}


 (2)、Object.assign()

  • 参数1:一个空对象,用来存放新增属性与原对象混合之后的对象 
  • 参数2:原来的对象
  • 参数3:需要给对象新增的属性

this.item = Object.assign({},this.item,{newProperty:'新值'})

 (3)、$forcecUpdated

在Vue实例添加这个方法 

this.$forcecUpdated()

强制Vue实例进行重新渲染

四、小结。

  • 如果需要添加少量属性,可直接采用Vue.set()
  • 如果需要添加大量属性,就采用 Object.assign()
  • 强制刷新采用$forcecUpdated()(不推荐)

PS:vue3是用过proxy实现数据响应式的,直接动态添加新属性仍可以实现数据响应式 

Logo

前往低代码交流专区

更多推荐