Vue3 Composition API中向数组推入对象时数据被覆盖的问题排查
为什么添加第二个选择项时已添加的第一个会被覆盖?
问题场景
我做了一个包含两个选择项的功能:
- size选项:可选large、small(代码里实际用了Standart、Big)
- quantity选项:可选2/4/6(代码里实际用了6、8、12)
选中选项后头部会展示所选内容,点击「add to storage」按钮把数据存入存储,之后在另一页面展示列表。
我的尝试代码:
Vue TS 逻辑代码
const chosenMachines = reactive([]); const machineInfo = ref({ size: "Standart", glasses: 6 }) const addToList = () => { // myStore.addToStoredItems(machineInfo) ---> 计划用Pinia存储 chosenMachines.push(machineInfo.value) }
模板代码
<div>Title {{ machineInfo.size }} | {{ machineInfo.glasses }}</div> <select name="size" v-model='machineInfo.size' class='selects__select-item'> <option>Standart</option> <option>Big</option> </select> <select name="glasses" v-model.number='machineInfo.glasses' class='selects__select-item'> <option>6</option> <option>8</option> <option>12</option> </select> <button class='btn' @click='sendToStorage'>Add</button>
现在遇到的问题:添加第一个对象后,再选新的选项添加第二个,第一个对象的内容会被改成新选项的值,看起来像是被覆盖了。
原因分析
核心问题是对象引用共享:
machineInfo.value是一个固定的对象,你每次调用addToList时,都是把这个对象的引用push到chosenMachines数组里。- 数组里的所有元素其实都指向同一个内存地址的对象,当你通过select修改
machineInfo.value的属性时,数组里所有引用这个对象的元素都会同步变化,导致之前添加的项看起来被覆盖了。
另外还有一个小问题:模板里按钮的点击事件是sendToStorage,但逻辑里定义的方法是addToList,这会导致点击按钮没有反应,需要统一方法名。
解决方案
每次添加时,创建一个当前machineInfo.value的对象副本,把副本push到数组里,这样每个元素都是独立的对象,不会互相影响。
修改后的addToList方法:
const addToList = () => { // 用扩展运算符创建对象副本 chosenMachines.push({...machineInfo.value}) // 也可以用Object.assign实现:Object.assign({}, machineInfo.value) // 如果用Pinia存储,同样要存副本:myStore.addToStoredItems({...machineInfo.value}) }
同时修正模板里的按钮点击事件:
<button class='btn' @click='addToList'>Add</button>
这样每次添加的都是独立的对象,后续修改select的值只会影响当前的machineInfo,不会改变数组里已有的项。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

