Vue组合式API选型:暴露操作函数还是接收响应式状态?
Vue Composition API:Composable两种状态更新模式的选择
在使用Vue的Composition API编写composable时,常会遇到两种状态更新模式的选择:
- 从composable返回操作状态的函数,由外部调用触发更新
- 让composable接收外部响应式状态,自动响应依赖变化更新内部状态
以「根据用户名筛选获取用户列表」的场景为例,两种模式的代码实现及适用场景分析如下:
模式1:返回响应式列表及操作函数
这种模式下,composable内部完全管理状态,外部通过调用返回的函数显式触发更新:
<script setup lang="ts"> const { list, setList } = useUserList(); </script> <template> <pre>{{ list }}</pre> <button type="button" @click="setList('john')">List users named "John"</button> </template>
对应的composable实现示例:
import { ref } from 'vue'; import { fetchUsersByName } from '@/api'; export function useUserList() { const list = ref<User[]>([]); const setList = async (name: string) => { // 可在此添加前置校验、加载状态管理、错误捕获等逻辑 list.value = await fetchUsersByName(name); }; return { list, setList }; }
适用场景:
- 更新由显式事件触发(如按钮点击、表单提交)
- 需要在更新前执行额外逻辑(如参数校验、状态切换)
- 希望composable完全封装状态修改逻辑,外部仅关心调用入口
模式2:基于输入响应式依赖返回响应式列表
这种模式下,composable接收外部响应式变量作为依赖,自动监听依赖变化并更新内部状态:
<script setup lang="ts"> const nameFilter = ref(''); const { list } = useUserList(nameFilter); </script> <template> <pre>{{ list }}</pre> <button type="button" @click="nameFilter = 'John'">List users named "John"</button> </template>
对应的composable实现示例:
import { ref, watch, onMounted } from 'vue'; import { fetchUsersByName } from '@/api'; export function useUserList(nameFilter: Ref<string>) { const list = ref<User[]>([]); const fetchList = async () => { list.value = await fetchUsersByName(nameFilter.value); }; // 监听依赖变化自动触发更新 watch(nameFilter, fetchList); // 初始化时自动触发一次请求 onMounted(fetchList); return { list }; }
适用场景:
- 更新由依赖变化自动触发(如输入框实时过滤、路由参数变化、其他状态同步)
- 希望以声明式方式管理依赖,减少手动触发代码
- 外部需要直接控制依赖变量,且依赖变化频繁
核心决策原则
- 事件驱动选模式1:如果更新是用户操作等离散事件触发,显式调用函数逻辑更清晰,也方便在composable内封装复杂逻辑。
- 依赖驱动选模式2:如果更新是响应式变量变化触发,利用Vue的响应式系统自动同步,代码更简洁,符合声明式编程思维。
- 复杂场景可混合:如果需要同时支持两种触发方式,可让composable接收可选的响应式依赖,同时返回操作函数,兼顾灵活性:
export function useUserList(nameFilter?: Ref<string>) { const list = ref<User[]>([]); const internalFilter = ref(''); const currentFilter = nameFilter ?? internalFilter; const fetchList = async () => { list.value = await fetchUsersByName(currentFilter.value); }; if (nameFilter) { watch(nameFilter, fetchList); } onMounted(fetchList); const setList = (name: string) => { internalFilter.value = name; fetchList(); }; return { list, setList }; }
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

