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 的优缺点:

优点:

  1. 直接访问:不需要 .value
  2. 更直观:对于对象/数组,代码更简洁
  3. 性能稍好:Vue 内部优化

缺点:

  1. 不能解构:解构会失去响应性
  2. 类型限制:只能是对象类型(对象、数组、Map、Set)
  3. 替换麻烦:不能直接赋值 = [],需要用 splice

ref 的优缺点:

优点:

  1. 更灵活:可以存储任何类型的值
  2. 直接赋值:可以 allotVO.value = []
  3. 解构方便:在组合式函数中更容易返回
  4. 类型支持更好:TypeScript 类型推断更准确

缺点:

  1. 需要 .value:访问和修改都要加 .value
  2. 代码稍繁琐:多了 .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,因为:

  1. 你明显需要清空和重新赋值操作
  2. refallotVO.value = []reactivesplice 更直观
  3. 现代 Vue 3 项目中更倾向于使用 ref
  4. 组合式 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

因为 allotVOref,所以访问它的值需要使用 .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 需要 .valueallotVO.value.forEach
  • reactive 不需要 .valueallotVO.forEach
  • 推荐用 map 一步完成复制和修改
posted @ 2026-01-09 15:36  VipSoft  阅读(9)  评论(0)    收藏  举报