vue 3 的 map/set 响应式需显式包裹(如 reactive(new map())),仅劫持原生方法调用,不自动深层响应内部对象属性变化,且 weakmap/weakset 不支持响应式。

Vue 3 的响应式系统原生支持 Map 和 Set,但不是“自动深层响应”,而是通过包装类(ReactiveMap/ReactiveSet)重写原型方法来实现精准劫持——结构变更能响应,内部值变化需手动处理。
必须显式包裹才能获得响应式能力
裸露的 new Map() 或 new Set() 赋值给 reactive 对象属性,不会触发更新。正确做法是:
-
推荐用
reactive():如const myMap = reactive(new Map()),后续调用myMap.set()、myMap.delete()等原生方法会自动通知依赖 -
Pinia 中常用
ref():如permissions: ref(new Map()),读写都需加.value(this.permissions.value.set()) -
禁止直接赋值:
state: { cache: new Map() }→ 即使调用cache.set()也不响应
只响应结构操作,不监听内部属性变化
Map/Set 的响应式边界在“增删改查”动作本身,而非其存放对象的字段变动:
-
myMap.set('user', { name: 'Alice' })→ 修改user.name不触发模板重渲染 -
myMap.set('user', reactive({ name: 'Alice' }))→user.name变化会触发相关computed或effect,但不会让v-for重新执行,除非你再次读取myMap.get('user') -
mySet.add({ id: 1, count: 0 })→count改变无响应;改为add(reactive({ id: 1, count: 0 }))后才生效
必须走原生方法,绕过代理就失效
所有变更必须调用集合自身的 API,否则 Proxy 拦截无法捕获:
-
✅ 正确:
map.set(key, val)、set.add(item)、map.delete(key)、set.clear() -
❌ 无效:
map[key] = val(跳过 Proxy)、set[0] = item(Set 不支持索引)、手动改map.size -
⚠️ 遍历注意:
v-for="[key, value] of map"安全;Array.from(map)[0] = ...会脱离响应链
Key 和 value 类型影响稳定性与可追踪性
尤其是 Map 的 key 和 Set 的 value,类型选择直接影响响应行为:
-
Map 的 key 应为原始值(字符串/数字/Symbol)或同一个
reactive对象实例;避免每次new Object()作 key,会导致has()/get()失效 -
Set 的 value 若是对象,重复
add同一引用不会触发更新(符合语义),但不同引用即使内容相同也会被当作新值 - WeakMap / WeakSet 不支持响应式:Vue 3 明确不劫持,因其弱引用行为无法被 Proxy 安全拦截
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











