Vue3中过滤响应式数组后修改新数组为何会更新原数组?
Vue3中过滤响应式数组后修改新数组元素影响原数组的原因分析
核心原因:数组元素是对象引用,而非独立副本
JavaScript的filter方法确实会创建一个全新的数组实例,但这个新数组中的元素并非原数组对象的深拷贝——它们是原数组中符合条件对象的内存引用。也就是说,新旧数组里的对应元素指向的是同一个对象实例,修改其中一个的属性,本质上就是修改这个对象本身,自然会同步反映到原数组的对应元素上。
结合Vue3响应式的补充说明
Vue3基于Proxy实现响应式系统,当你将一个数组通过reactive转为响应式后,数组内的对象也会被转为响应式对象(默认是浅响应式)。无论你是从原响应式数组,还是从过滤后的新数组访问这个对象,操作的都是同一个响应式对象。修改对象属性时,Proxy会捕获到这个变化并触发视图更新,原数组的元素自然也会同步变化。
示例代码验证(模拟问题场景)
假设你有如下响应式数组:
import { reactive } from 'vue' const originalList = reactive([ { id: 1, name: 'Vue', completed: false }, { id: 2, name: 'React', completed: true }, { id: 3, name: 'Angular', completed: true } ]) // 过滤得到新数组 const filteredList = originalList.filter(item => item.completed) // 修改新数组元素的属性 filteredList[0].completed = false // 此时原数组的第二个元素(id=2)的completed也会变为false console.log(originalList[1].completed) // 输出: false
解决办法:对元素进行深拷贝
如果希望过滤后的新数组元素与原数组完全独立,需要对每个元素执行深拷贝,切断引用关系。常见的实现方式有:
- 使用原生API
structuredClone(支持大多数数据类型,包括循环引用):const filteredList = originalList.filter(item => item.completed).map(item => structuredClone(item)) - 使用
JSON.parse(JSON.stringify(item))(注意:无法处理函数、Symbol、循环引用等特殊类型):const filteredList = originalList.filter(item => item.completed).map(item => JSON.parse(JSON.stringify(item))) - 使用Vue提供的
cloneDeep工具(从@vue/shared包导入):import { cloneDeep } from '@vue/shared' const filteredList = originalList.filter(item => item.completed).map(item => cloneDeep(item))
内容的提问来源于stack exchange,提问作者Craig
相关产品推荐
相关产品推荐

