Vue中给对象添加新属性时,界面不刷新怎么办?

简介: Vue中给对象添加新属性时,界面不刷新怎么办?

前言

hello world欢迎来到前端的新世界


😜当前文章系列专栏:vue.js

🐱‍👓博主在前端领域还有很多知识和技术需要掌握,正在不断努力填补技术短板。(如果出现错误,感谢大家指出)🌹

💖感谢大家支持!您的观看就是作者创作的动力

直接添加属性的问题


我们从一个例子开始


定义一个p标签,通过v-for指令进行遍历


然后给botton标签绑定点击事件,我们预期点击按钮时,数据新增一个属性,界面也 新增一行


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


实例化一个vue实例,定义data属性和methods方法


const app = new Vue({
    el:"#app",
    data:()=>{
        item:{
            oldProperty:"旧属性"
        }
    },
    methods:{
        addProperty(){
            this.items.newProperty = "新属性"  // 为items添加新属性
            console.log(this.items)  // 输出带有newProperty的items
        }
    }
})


点击按钮,发现结果不及预期,数据虽然更新了(console打印出了新属性),但页面并没有更新

原理分析


为什么产生上面的情况呢?


下面来分析一下


vue2是用过Object.defineProperty实现数据响应式


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


当我们访问foo属性或者设置foo值的时候都能够触发setter与getter


obj.foo   
obj.foo = 'new'


但是我们为obj添加新属性的时候,却无法触发事件属性的拦截


obj.bar  = '新属性'


原因是一开始obj的foo属性被设成了响应式数据,而bar是后面新增的属性,并没有通过Object.defineProperty设置成响应式数据

解决方案

Vue 不允许在已经创建的实例上动态添加新的响应式属性


若想实现数据与视图同步更新,可采取下面三种解决方案:


  • Vue.set()
  • Object.assign()
  • $forcecUpdated()

Vue.set()


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

参数


  • {Object | Array} target
  • {string | number} propertyName/index
  • {any} value


返回值:设置的值


通过Vue.set向响应式对象中添加一个property,并确保这个新 property 同样是响应式的,且触发视图更新

关于Vue.set源码(省略了很多与本节不相关的代码)


源码位置:src\core\observer\index.js


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

这里无非再次调用defineReactive方法,实现新增属性的响应式


关于defineReactive方法,内部还是通过Object.defineProperty实现属性拦截


大致代码如下:


function defineReactive(obj, key, val) {
    Object.defineProperty(obj, key, {
        get() {
            console.log(`get ${key}:${val}`);
            return val
        },
        set(newVal) {
            if (newVal !== val) {
                console.log(`set ${key}:${newVal}`);
                val = newVal
            }
        }
    })
}


Object.assign()


直接使用Object.assign()添加到对象的新属性不会触发更新


应创建一个新的对象,合并原对象和混入对象的属性


this.someObject = Object.assign({},this.someObject,{newProperty1:1,newProperty2:2 ...})


$forceUpdate


如果你发现你自己需要在 Vue 中做一次强制更新,99.9% 的情况,是你在某个地方做错了事


$forceUpdate迫使 Vue 实例重新渲染


PS:仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。

总结

  • 如果为对象添加少量的新属性,可以直接采用Vue.set()
  • 如果需要为新对象添加大量的新属性,则通过Object.assign()创建新对象
  • 如果你实在不知道怎么操作时,可采取$forceUpdate()进行强制刷新 (不建议)


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


后言

创作不易,要是本文章对广大读者有那么一点点帮助 不妨三连支持一下,您的鼓励就是博主创作的动力


目录
相关文章
|
1天前
|
JavaScript 前端开发 容器
Vue 3 中 <transition-group> 组件报错的非 props 属性传递问题
Vue 3 中 <transition-group> 组件报错的非 props 属性传递问题
12 1
|
1天前
|
移动开发 前端开发
ruoyi-nbcio-plus基于vue3的flowable扩展属性的升级修改
ruoyi-nbcio-plus基于vue3的flowable扩展属性的升级修改
|
1天前
|
JavaScript
vue打印v-model 的值
vue打印v-model 的值
|
1天前
|
JavaScript
vue知识点
vue知识点
4 1
|
2天前
|
JavaScript
Vue实战-组件通信
Vue实战-组件通信
4 0
|
2天前
|
JavaScript
Vue实战-将通用组件注册为全局组件
Vue实战-将通用组件注册为全局组件
5 0
|
JavaScript 测试技术 容器
Vue2+VueRouter2+webpack 构建项目
1). 安装Node环境和npm包管理工具 检测版本 node -v npm -v 图1.png 2). 安装vue-cli(vue脚手架) npm install -g vue-cli --registry=https://registry.
987 0
|
3天前
|
存储 JavaScript
Vue当前时间与接口返回时间的判断
Vue当前时间与接口返回时间的判断
8 0
|
2天前
|
JavaScript 前端开发
vue的论坛管理模块-文章评论02
vue的论坛管理模块-文章评论02
|
2天前
|
JavaScript Java
vue的论坛管理模块-文章查看-01
vue的论坛管理模块-文章查看-01