Appearance
Vue 3 响应式 Map 全攻略
本节目标:理解“Vue 3 响应式 Map 全攻略”的核心思路,并能把它用于实际开发或面试表达。 方案清单
| 场景 | 推荐方案 | 代码示例 | 代价 / 注意点 |
| A. 只关心行的增删 | |||
| 例如列表渲染组件 | 继续用 Array.from(map.values()) | ts const rows = computed(() => Array.from(openOrderData.values())) | 性能好;行内部更新不会触发。 |
| B. 需要行内容一有变动就刷新 UI | 在 computed 里显式展开行对象(最简) | ts const rows = computed(() => Array.from(openOrderData.values()).map(r => ({ ...r }))) | 每次计算都会浅拷贝;Map 很大时有额外 GC 压力。 |
| 或:用 watchEffect 深度读取指定字段 | ts watchEffect(() => { openOrderData.forEach(r => { void r.price; void r.qty }) }) | 手动列出依赖字段,可按需优化。 | |
| C. 只关心某些字段 | |||
| 避免拷贝整个行 | 把 关键字段映射成派生数组 | ts const prices = computed(() => [...openOrderData.values()].map(r => r.price)) | 依赖量小,但要同时维护行和字段数组的关系。 |
| D. 性能极敏感 | |||
| 且行对象巨大 | 1️⃣ 给行包一层 ref(行变动时用 rowRef.value = {…}) | ||
| 2️⃣ 或把 Map ➜ Reactive Record(普通对象) | ts const rowRef = ref<Row>() openOrderData.set(id, rowRef) | 代码更啰嗦,牺牲一点语义清晰度换性能可控。 |
4. 常见坑 & 对策
| 症状 | 根因 | 修复思路 |
| 行对象属性变了,UI 不更新 | 没有依赖收集到具体属性 | 方案 B / C / D |
| Object.assign 后依旧不触发 | 被赋值的字段值相同或没收集依赖 | 确认字段真的变化;改用新对象引用 |
| 替换整行也不触发 | 直接 map.set(key, newRow) 正常会触发;若没触发,检查 newRow === oldRow | 确保提供全新引用 |
| 大量条目更新造成卡顿 | computed 每次做浅拷贝 | 改成 watchEffect 精准追踪 (void r.xxx) 或用 ref 包装行 |
5. 代码片段模板(复制即用)5.1 统一封装更新函数 function upsertRow(map: Map<number, Row>, row: Row) { const exist = map.get(row.id); if (exist) { // ⚠️ 优先替换引用,触发次数最少 map.set(row.id, { ...exist, ...row }); } else { map.set(row.id, row); } }
5.2 带依赖收集的 computed // 收集全部列(适合表格场景) const tableRows = computed(() => Array.from(openOrderData.values()).map(r => ({ ...r })) );
5.3 精准监听字段(大数据量场景) watchEffect(() => { openOrderData.forEach(r => { // 只读关键字段,Vue 会对这些字段建立依赖 void r.status; void r.lastPrice; }); });
6. 结语
- 先想依赖、再写 computed —— 牢记 Vue 只在 你读取的地方 建立追踪。
- Map ≠ 普通对象:Vue 不会帮你深拷贝或深监听,你要手动“触摸”字段。
- 三板斧:
- 确认场景(只关心结构?还是行内容?)
- 选方案(A/B/C/D)
- 验证依赖(console.log + Vue DevTools)
有了这份教程,下次遇到“Map 里改了数据 UI 不刷”的问题,只需按照 判断流程 → 方案清单 两步走,很快就能定位并解决💪。祝编码顺利!
