You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 事件驱动选模式1:如果更新是用户操作等离散事件触发,显式调用函数逻辑更清晰,也方便在composable内封装复杂逻辑。
  2. 依赖驱动选模式2:如果更新是响应式变量变化触发,利用Vue的响应式系统自动同步,代码更简洁,符合声明式编程思维。
  3. 复杂场景可混合:如果需要同时支持两种触发方式,可让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 23:10:03