Vue ref reactive
问题1
const allotVO = reactive<DeviceInfoVO[]>([]);
allotVO = [];
不能对 allotVO 进行初始化,怎么能把 allotVO 里面的数据给清掉。
直接修改数组长度(推荐)
// 方法1 - 最简洁
allotVO.length = 0;
// 或者方法2 - 更明确
allotVO.splice(0);
问题2
const selectedItems = ref<DeviceInfoVO[]>([]);
const allotVO = ref<DeviceInfoVO[]>([]);
怎么把 selectedItems 赋值给 allotVO,可以直接等吗。
为什么不能直接等?
// 解构赋值(推荐)
allotVO.value = [...selectedItems.value];
// ❌ 错误做法 - 会共享引用
allotVO.value = selectedItems.value;
// 这样修改 allotVO 也会影响 selectedItems
allotVO.value.push(someItem); // selectedItems 也会被修改!
深层次对象拷贝
// 浅拷贝(对于一层结构足够)
allotVO.value = [...selectedItems.value];
// 深拷贝(如果对象有嵌套)
import { cloneDeep } from 'lodash-es';
allotVO.value = cloneDeep(selectedItems.value);
// 或者使用 JSON(有局限性,不能处理函数、循环引用等)
allotVO.value = JSON.parse(JSON.stringify(selectedItems.value));
赋值问题
const selectedItems = ref<DeviceInfoVO[]>([]);
const allotVO = reactive<DeviceInfoVO[]>([]);
Object.assign(allotVO, { ...selectedItems.value });
我第一次选10条,第二次选2条。最后 allotVO 还是 10条
原因分析
// 第一次:selectedItems 有10条
selectedItems.value = [item1, item2, ..., item10]; // 10条
Object.assign(allotVO, { ...selectedItems.value });
// 结果:allotVO 变成 {0:item1, 1:item2, ..., 9:item9},length=10
// 第二次:selectedItems 有2条
selectedItems.value = [itemA, itemB]; // 2条
Object.assign(allotVO, { ...selectedItems.value });
// 结果:allotVO 变成 {0:itemA, 1:itemB, 2:item3, ..., 9:item9}
// 因为 Object.assign 只覆盖已有的属性,不会删除多余的属性!
// 所以 allotVO.length 仍然是 10,后面8个是旧数据
正确的解决方案:
方案1:使用 splice(最推荐)
// 清空并替换
allotVO.splice(0, allotVO.length, ...selectedItems.value);
方案2:先清空再push
allotVO.length = 0;
allotVO.push(...selectedItems.value);
方案3:如果需要用 Object.assign
// 必须先清空所有属性
allotVO.splice(0); // 清空数组
// 然后再赋值
selectedItems.value.forEach((item, index) => {
allotVO[index] = item;
});
总结:
Object.assign只是合并属性,不会删除多余的属性- 对于数组操作,应该使用数组的原生方法
- 使用
splice可以确保完全替换数组内容
reactive 的优缺点:
优点:
- 直接访问:不需要
.value - 更直观:对于对象/数组,代码更简洁
- 性能稍好:Vue 内部优化
缺点:
- 不能解构:解构会失去响应性
- 类型限制:只能是对象类型(对象、数组、Map、Set)
- 替换麻烦:不能直接赋值
= [],需要用splice
ref 的优缺点:
优点:
- 更灵活:可以存储任何类型的值
- 直接赋值:可以
allotVO.value = [] - 解构方便:在组合式函数中更容易返回
- 类型支持更好:TypeScript 类型推断更准确
缺点:
- 需要
.value:访问和修改都要加.value - 代码稍繁琐:多了
.value
具体建议:
使用 reactive 的场景:
// 场景1:作为组件内部状态,不经常重新赋值
const formState = reactive({
name: '',
age: 0,
list: [] as DeviceInfoVO[]
});
// 场景2:嵌套在对象中的数组
const state = reactive({
allotList: [] as DeviceInfoVO[],
selectedList: [] as DeviceInfoVO[],
loading: false
});
// 使用:state.allotList.push(item)
使用 ref 的场景:
// 场景1:需要频繁重新赋值的数组
const allotVO = ref<DeviceInfoVO[]>([]);
// 清空:allotVO.value = []
// 赋值:allotVO.value = [...selectedItems.value]
// 场景2:在组合式函数中返回
function useDeviceList() {
const list = ref<DeviceInfoVO[]>([]);
const clear = () => {
list.value = []; // 直接赋值很方便
};
return { list, clear }; // 返回 ref 更方便
}
// 场景3:需要模板自动解包
// 模板中可以直接使用 allotVO 而不是 allotVO.value
针对你的情况:
如果你需要:
- 频繁清空和重新赋值 → 用
ref - 主要是 push/pop/splice 操作 → 用
reactive也可以
推荐方案:
// 如果经常需要 allotVO = [] 或 allotVO = newArray
const allotVO = ref<DeviceInfoVO[]>([]); // ✅ 推荐
// 如果主要是数组方法操作
const allotVO = reactive<DeviceInfoVO[]>([]); // 也可以
// 或者结合使用(最灵活)
const state = reactive({
allotVO: [] as DeviceInfoVO[],
// 其他状态...
});
// 清空和赋值可以用辅助函数
const clearAllotVO = () => {
state.allotVO.splice(0);
};
const setAllotVO = (items: DeviceInfoVO[]) => {
state.allotVO.splice(0, state.allotVO.length, ...items);
};
结论:
对于你的 allotVO,我推荐使用 ref,因为:
- 你明显需要清空和重新赋值操作
ref的allotVO.value = []比reactive的splice更直观- 现代 Vue 3 项目中更倾向于使用
ref - 组合式 API 中
ref更通用
// 最终建议
const selectedItems = ref<DeviceInfoVO[]>([]);
const allotVO = ref<DeviceInfoVO[]>([]); // 改用 ref
// 使用
allotVO.value = [...selectedItems.value]; // 赋值
allotVO.value = []; // 清空
allotVO.value.push(item); // 添加
allotVO.value = [...selectedItems.value];
// 根据条件遍历赋值
allotVO.forEach((item) => {
// 示例:根据索引或其他逻辑赋值
item.HospitalId = dept.HospitalId;
item.HospitalName = dept.HospitalName;
item.HospitalShortName = dept.HospitalShortName;
item.DeptId = dept.Id;
item.DeptName = dept.DeptName;
});
是不是得用 allotVO.value.forEach
是的,必须用 allotVO.value.forEach!
因为 allotVO 是 ref,所以访问它的值需要使用 .value:
// ✅ 正确
allotVO.value.forEach((item) => {
item.HospitalId = dept.HospitalId;
item.HospitalName = dept.HospitalName;
item.HospitalShortName = dept.HospitalShortName;
item.DeptId = dept.Id;
item.DeptName = dept.DeptName;
});
// ❌ 错误:allotVO 是 ref,没有 forEach 方法
allotVO.forEach((item) => { ... });
完整正确代码:
// 定义 ref
const allotVO = ref<DeviceInfoVO[]>([]);
// 赋值
allotVO.value = [...selectedItems.value];
// 遍历修改每一项
allotVO.value.forEach((item) => {
item.HospitalId = dept.HospitalId;
item.HospitalName = dept.HospitalName;
item.HospitalShortName = dept.HospitalShortName;
item.DeptId = dept.Id;
item.DeptName = dept.DeptName;
});
如果你用的是 reactive:
// 如果用 reactive(不需要 .value)
const allotVO = reactive<DeviceInfoVO[]>([]);
// 赋值(需要 splice 而不是直接 =)
allotVO.splice(0, allotVO.length, ...selectedItems.value);
// 遍历(直接使用 allotVO,没有 .value)
allotVO.forEach((item) => {
item.HospitalId = dept.HospitalId;
// ... 其他赋值
});
简化建议:
你可以考虑使用 map 一步完成赋值和修改:
// 一步完成:复制数组并修改每一项
allotVO.value = selectedItems.value.map(item => ({
...item, // 保留原有属性
HospitalId: dept.HospitalId,
HospitalName: dept.HospitalName,
HospitalShortName: dept.HospitalShortName,
DeptId: dept.Id,
DeptName: dept.DeptName
}));
这样更简洁,而且不会修改原始的 selectedItems.value。
总结:
ref需要.value→allotVO.value.forEachreactive不需要.value→allotVO.forEach- 推荐用
map一步完成复制和修改
本文来自博客园,作者:VipSoft 转载请注明原文链接:https://www.cnblogs.com/vipsoft/p/19461765
浙公网安备 33010602011771号