对象或数组对象赋值时,响应式失效

当我们在vue项目中,这样写代码时,可能回出现响应式失效的情况:

	export default {
		data(){
			return {
				obj: { a:'', b: '' }, 
				arr: []
			}
		},
		methods: {
		/*
		如果你尝试给对象中不存在的属性赋值,Vue 并不能自动监听这个新属性的变化,因为它不知道这个属性的存在。
	*/
			changeObj(){
				this.obj.c = 'ccc'
			},
			/*
				为了让 Vue 监听新属性,你可以使用 Vue.set 或 this.$set 方法来进行赋值,这样 Vue 就会在新属性上建立响应式追踪。
			*/
			changeObjThoughSet(){
			this.$set(this.obj, 'c', 'c')
		},
			/*直接通过索引给数组中的元素赋值,Vue 不能自动监听这个操作。这是因为修改数组中的元素的索引不会触发数组本身的变化,所以 Vue 无法检测到。*/
			changeArrThroughIndex(index){
					this.arr[index].a = 1
			},
			/*
			数组的响应式追踪会通过修改数组的原型链来实现,以便捕捉数组的变化。这包括 push、pop、shift、unshift、splice 和 sort 等方法。这些方法会触发数组的响应式更新。
			*/
			changeArrThroughPush(){
				this.arr.push({a:'1'})
			}
		}
	}

vue2响应式原理

vue是如何追踪数据变化形成响应式的

简略描述:
页面一进来会扫描数据,使其可以实现vue的双向绑定功能,当初始值没有设定的属性,后面添加新属性,会存在数据变化而页面不同步的问题。

详细:
当你把你在 data(){}中定义一个object对象时,Vue将遍历此对象所有的property, 并使用Object.defineProperty把这些property全部转为getter/setter
这些getter/setter对用户时不可见的,但是在内部它们让Vue能够追踪依赖,在property被访问和修改时通知变更。
每个组件实例都对应一个Watcher实例,它会在组件渲染过程中把它“接触过”的数据property记录为依赖。之后当依赖项setter触发时,会通知watcher,从而使它关联组件重新渲染。

解决方案

对象

1. 初始时设定
2. Vue.set
3. 创建一个新对象,替换原对象

这种方法可以用于需要添加多个新属性,再把原对象与新属性合并到新对象中
Object.assign(目标对象,原对象, 新属性)

数组

1. Vue.set
2. 切割替换原数组

vm.items.splice(indexOfItem, 1, newValue)

2. 拷贝

this.arr = arr.slice()

对象,数组通用

拷贝一个新对象,让vue重新建立新的响应式关系

Vue2 的响应式失效问题通常出现在对象深层嵌套时,因为 Vue2 无法自动追踪嵌套对象的变化。为了解决这个问题,可以使用JSON.parse(JSON.stringify())这种方式来深拷贝对象,从而让 Vue2 重新建立响应式关系。

这个原理的关键在于,JSON.stringify()方法会将一个对象转换成一个 JSON 字符串,而JSON.parse()方法则会将这个 JSON 字符串解析成一个新的 JavaScript 对象。通过这两个方法的组合,你实际上创建了一个完全独立于原始对象的新对象,这个新对象会重新被 Vue2 追踪并建立响应式关系。

this.objData = JSON.parse(JSON.stringify(data));

然而,需要注意的是,这种方法有一些限制:

  1. 函数、RegExp、Date 等特殊对象类型会失去原有的类型。
  2. 如果对象内部包含循环引用,会导致JSON.stringify()无法正常工作。
    因此,这种方法适用于大多数简单的对象,但对于复杂的数据结构,可能需要考虑其他深拷贝方法,或者手动修复响应式失效的问题。

在 Vue3 中,这个问题已经得到了较好的解决,因为 Vue3 使用了 Proxy 来实现更强大的响应式系统,可以自动追踪对象内部的变化,不再需要使用JSON.parse(JSON.stringify())这样的技巧来维护响应式关系。

Logo

前往低代码交流专区

更多推荐