如何在Vue3中高效实现数组特定值的重置?
Vue3 数组内特定值高效重置的几种实现方法
你的现有方案通过structuredClone存储初始值并批量重置的思路是可行的,下面提供几种不同场景下的优化和替代方案:
方法1:基于原始数据源的直观重置
将原始数据作为独立常量存储,重置时通过ID匹配原始值,结构更清晰,便于扩展重置其他字段:
<script setup> import { ref, watch } from 'vue'; const selected = ref(null) // 原始数据源(常量,不修改) const ORIGINAL_DATA = [ { id: 1, label: 'A', value: 'A', name: 'Student A' }, { id: 2, label: 'B', value: 'B', name: 'Student B' }, { id: 3, label: 'C', value: 'C', name: 'Student C' } ] const data = ref([...ORIGINAL_DATA]) watch(selected, (v) => { if (v === null) { // 匹配ID重置为原始值 data.value.forEach(item => { const original = ORIGINAL_DATA.find(o => o.id === item.id) if (original) item.value = original.value }) } else { data.value.forEach(item => item.value = v) } }) const updateValue = (id, newValue) => { const item = data.value.find(item => item.id === id) if (item) item.value = newValue } </script> <template> <div> <select v-model="selected"> <option :value="null">Init Value</option> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> <template v-for="item in data" :key="item.id"> <!-- 用ID作为key,避免index的潜在问题 --> <div style="display:flex ; align-items: center;margin-bottom: 16px;"> <div style="margin-right:16px;"> Name:{{ item.name }}</div> <select :value="item.value" @change="updateValue(item.id, $event.target.value)"> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> </div> </template> </div> </template>
方法2:响应式状态拆分(符合Vue设计思想)
通过计算属性拆分基础数据、全局选中状态、自定义修改状态,无需手动遍历修改数组,纯响应式推导:
<script setup> import { ref, computed } from 'vue'; const globalSelected = ref(null) // 基础数据 const BASE_DATA = [ { id: 1, label: 'A', value: 'A', name: 'Student A' }, { id: 2, label: 'B', value: 'B', name: 'Student B' }, { id: 3, label: 'C', value: 'C', name: 'Student C' } ] // 存储用户自定义修改的值 const customValues = ref({}) // 计算最终展示数据:自定义值 > 全局选中值 > 原始值 const data = computed(() => { return BASE_DATA.map(item => { const finalValue = customValues.value[item.id] ?? globalSelected.value ?? item.value return { ...item, value: finalValue } }) }) const updateValue = (id, newValue) => { customValues.value[id] = newValue // 修改单个值后清空全局选中,避免状态冲突 if (globalSelected.value !== null) { globalSelected.value = null } } </script> <template> <div> <select v-model="globalSelected"> <option :value="null">Init Value</option> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> <template v-for="item in data" :key="item.id"> <div style="display:flex ; align-items: center;margin-bottom: 16px;"> <div style="margin-right:16px;"> Name:{{ item.name }}</div> <select :value="item.value" @change="updateValue(item.id, $event.target.value)"> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> </div> </template> </div> </template>
方法3:不可变数据模式(适合状态追踪场景)
如果需要追踪数据变化历史或配合状态管理库,可使用不可变数据模式,每次更新生成新数组:
原生Spread版本
<script setup> import { ref, watch } from 'vue'; const selected = ref(null) const ORIGINAL_DATA = [ { id: 1, label: 'A', value: 'A', name: 'Student A' }, { id: 2, label: 'B', value: 'B', name: 'Student B' }, { id: 3, label: 'C', value: 'C', name: 'Student C' } ] const data = ref([...ORIGINAL_DATA]) watch(selected, (v) => { if (v === null) { data.value = data.value.map(item => { const original = ORIGINAL_DATA.find(o => o.id === item.id) return original ? { ...item, value: original.value } : item }) } else { data.value = data.value.map(item => ({ ...item, value: v })) } }) const updateValue = (id, newValue) => { data.value = data.value.map(item => item.id === id ? { ...item, value: newValue } : item ) } </script> <template> <div> <select v-model="selected"> <option :value="null">Init Value</option> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> <template v-for="item in data" :key="item.id"> <div style="display:flex ; align-items: center;margin-bottom: 16px;"> <div style="margin-right:16px;"> Name:{{ item.name }}</div> <select :value="item.value" @change="updateValue(item.id, $event.target.value)"> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> </div> </template> </div> </template>
Immer简化版本
先安装依赖:npm install immer
<script setup> import { ref, watch } from 'vue'; import { produce } from 'immer'; const selected = ref(null) const ORIGINAL_DATA = [ { id: 1, label: 'A', value: 'A', name: 'Student A' }, { id: 2, label: 'B', value: 'B', name: 'Student B' }, { id: 3, label: 'C', value: 'C', name: 'Student C' } ] const data = ref([...ORIGINAL_DATA]) watch(selected, (v) => { data.value = produce(data.value, draft => { if (v === null) { draft.forEach(item => { const original = ORIGINAL_DATA.find(o => o.id === item.id) if (original) item.value = original.value }) } else { draft.forEach(item => item.value = v) } }) }) const updateValue = (id, newValue) => { data.value = produce(data.value, draft => { const item = draft.find(item => item.id === id) if (item) item.value = newValue }) } </script> <template> <div> <select v-model="selected"> <option :value="null">Init Value</option> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> <template v-for="item in data" :key="item.id"> <div style="display:flex ; align-items: center;margin-bottom: 16px;"> <div style="margin-right:16px;"> Name:{{ item.name }}</div> <select :value="item.value" @change="updateValue(item.id, $event.target.value)"> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> </select> </div> </template> </div> </template>
内容的提问来源于stack exchange,提问作者lizcodecode
相关产品推荐
相关产品推荐

