在 vue 3 setup 中定义局部状态应使用 ref 或 reactive 创建独立响应式变量:ref 适用于基础类型,每次调用生成新引用;reactive 用于对象结构,需确保每次创建新对象;避免状态提升和直接解构,应配合 torefs 保持响应性。

在 Vue 3 的 setup 函数中定义局部状态,核心是使用 ref 或 reactive 创建响应式变量,并确保每个实例或每个逻辑单元拥有自己独立的副本,而不是共享同一个引用。
用 ref 管理单个独立状态
ref 最适合管理基础类型(如布尔值、数字、字符串)或需要单独响应式的简单值。每次调用 ref 都会生成一个全新的响应式引用,天然隔离:
- 每个组件实例调用 setup 时,ref() 都会创建新对象,互不影响
- 避免直接用 let/const 声明普通变量——它不响应,也不参与更新
- 示例:为列表项控制“是否展开”,不要共用一个 isExpanded,而应在循环中为每项生成独立 ref
用 reactive 管理结构化局部状态
当状态是对象或嵌套结构时,用 reactive 更自然。但要注意:reactive 返回的是响应式代理对象,必须确保每次创建的是新对象,而非复用同一对象:
- 错误写法:
const itemState = reactive({ expanded: false })—— 若在多个地方复用这个对象,状态就共享了 - 正确做法:在 map 或循环中为每个项调用 reactive({ … }),或封装成工厂函数
- 例如:
items.map(item => ({ ...item, state: reactive({ loading: false, error: null }) }))
避免状态提升导致的“伪局部”问题
有时看似在 setup 里定义,实则把状态提到了父组件或全局作用域,造成意外共享:
- 不要在 setup 外声明 ref/reactive 变量再导入使用(除非明确设计为共享)
- 自定义 Hook 中若返回 ref,每次调用该 Hook 也应返回新 ref,而非缓存旧值
- 组合式 API 中,逻辑复用靠函数封装 + 参数传入,而不是靠外部变量注入
配合 toRefs 保持解构后仍响应
从 reactive 解构出属性会丢失响应性,需用 toRefs 包裹后再解构,否则赋值操作无效:
- ✅ 正确:
const { count, name } = toRefs(state) - ❌ 错误:
const { count } = reactive({ count: 0 })—— count 变成普通值,修改不触发更新 - 尤其在返回给模板时,toRefs 能保证解构后的变量依然可响应











