Vue 3 用函数组织组件逻辑的一组 API
Options API 按 data、methods、computed、watch 等选项组织代码。组件较大时,同一业务逻辑容易分散在多个选项中;复用逻辑通常还要依赖 Mixins,而 Mixins 存在数据来源不清晰、命名冲突等问题。
Composition API 允许将同一业务关注点的状态、计算属性、方法和生命周期放在一起,并进一步抽取成组合式函数(Composable)。
Composition API 并不是 Options API 的替代品。简单组件使用 Options API 仍然合理;复杂组件、逻辑复用较多或 TypeScript 项目通常更适合 Composition API。
setup()setup() 是 Composition API 的入口,在组件创建过程中、Options API 的大多数选项处理之前执行。
props第一个参数 props 是一个只读的响应式对象。不能直接修改它,也不能随意解构,否则可能丢失响应式关联。
如果只读取一次、不需要追踪后续变化,普通解构没有问题。Vue 3.5+ 的 <script setup> 还支持响应式 Props 解构,详见下文。
context第二个参数是 setup 上下文,可以安全解构:
| 属性 | 作用 |
|---|---|
attrs | 未声明为 props 或 emits 的透传属性和事件 |
slots | 访问父组件传入的插槽函数 |
emit | 触发组件事件 |
expose | 控制组件实例向父组件公开的内容 |
context 本身不是响应式对象。attrs 会保持最新值,但不能通过 watch() 追踪其属性变化;如果需要响应变化,应使用相应的 prop,或者在 onUpdated() 中处理。
setup() 通常返回一个对象,对象中的属性和方法可以在模板中使用。模板会自动解包顶层 ref,因此模板中不需要写 .value。
setup() 也可以直接返回渲染函数,此时不能再同时返回供模板使用的对象。
setup() 中没有this?执行 setup() 时,Options API 中的 data、computed 和 methods 等内容还没有初始化完成,因此不能通过组件公开实例的 this 访问它们。Vue 有意让 Composition API 使用变量、函数和参数显式组织依赖。
不要在 setup() 中混用 this:
反过来,Options API 可以通过 this 访问 setup() 返回的属性,但新代码不建议依赖这种混合方式。
<script setup><script setup> 是单文件组件中使用 Composition API 的推荐语法。代码会被编译为组件的 setup() 内容,顶层变量、函数和导入的组件可以直接在模板中使用。
defineProps、defineEmits、defineExpose 等是编译宏,只能在 <script setup> 中使用,不需要从 vue 导入。
常用宏包括:
| 宏 | 作用 |
|---|---|
defineProps() | 声明 props |
defineEmits() | 声明组件事件 |
withDefaults() | 为类型形式的 props 提供默认值 |
defineExpose() | 指定父组件通过模板 ref 可访问的内容 |
defineOptions() | 声明组件名、继承属性等组件选项 |
defineModel() | 声明组件的双向绑定,Vue 3.4+ 可用 |
defineSlots() | 为插槽提供 TypeScript 类型提示 |
在 Vue 3.5+ 中,<script setup> 对同一代码块中 defineProps() 解构出的变量进行响应式转换:
在 Vue 3.4 及更早版本中,直接解构不会保持响应式,应使用 props.title,或者通过 toRefs(props) 解构。维护旧版本项目时要先确认 Vue 版本。
Composition API 常用以下几类能力:
ref()、reactive():声明响应式状态computed():声明派生状态watch()、watchEffect():处理响应式副作用onMounted()、onUpdated()、onUnmounted():注册生命周期逻辑具体区别可阅读响应式 API、computed 与 watch和Vue 生命周期。
生命周期钩子必须在 setup() 同步执行期间注册,才能与当前组件实例关联:
Composable 是使用 Composition API 封装和复用有状态逻辑的函数,通常以 use 开头,例如 useMouse()、useFetch()。
onScopeDispose() 可以让清理逻辑跟随调用方的响应式作用域销毁。它比只能依赖组件卸载的写法更容易复用。
设计 Composable 时应注意:
| 对比项 | Mixins | Composable |
|---|---|---|
| 数据来源 | 合并到组件实例,来源不直观 | 通过函数返回值显式获得 |
| 命名冲突 | 多个 Mixin 可能冲突 | 调用方可以重命名变量 |
| 参数传递 | 通常依赖组件实例 | 使用普通函数参数 |
| TypeScript | 类型推导较困难 | 类型推导自然 |
| 逻辑关系 | 隐式合并 | 显式组合 |
Composable 复用的是逻辑,不是组件模板。需要同时复用 UI 结构时,应考虑组件或插槽。
不完全等同。它的组合式函数在使用体验上类似 React Hooks,但 Vue 依靠响应式系统追踪依赖,不依赖固定的调用顺序,也没有 React Hooks 的条件调用限制。
reactive() 吗?不是。ref() 可用于任意类型,也便于传递和替换;reactive() 适合将相关对象状态组织在一起。选择方式详见响应式 API。
setup() 等于created 吗?只能近似理解为都处于组件创建阶段,不能认为两者完全等价。setup() 的执行时机更早,并承担状态声明、逻辑组合和生命周期注册等职责。
纯响应式逻辑可以在普通模块中使用;依赖生命周期、provide/inject 或当前组件实例的 Composable,通常必须在 setup() 同步执行期间调用。
Composition API 是 Vue 3 用函数组织组件逻辑的一组 API。它通过 setup() 或 <script setup> 声明响应式状态、派生状态、副作用和生命周期,并可将有状态逻辑抽取为 Composable。
它的核心价值不是“代码更短”,而是让同一业务逻辑聚合、依赖来源明确、逻辑更容易复用和测试。相较 Mixins,Composable 具有显式输入输出、较少命名冲突和更好的 TypeScript 推导。实际开发中应根据组件复杂度选择 Composition API 或 Options API,并注意响应式解构、资源清理和 Vue 版本差异。