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

Vue3/Nuxt3组件如何向Composable传递含Props的响应式对象

问题:Vue中以对象形式传递props给Composable丢失响应式

我有一个使用Composition API的Vue组件,接收了若干props。我希望将这些props传入一个封装useFetch的Composable中,且当组件props变化时,Composable能保持响应式,即useFetch能重新执行。

以下是显式将props作为ref传入Composable的可行方案:

组件代码:

<script setup lang="ts">
const props = defineProps({
  page: {
    default: 1,
    required: false,
    type: Number,
  },
  search: {
    required: true,
    type: String,
  },
});

const { data, error, pending } = await useGetData(toRef(props, 'page'), toRef(props, 'search'));
</script>

Composable代码:

export default (page: Ref<number>, search: Ref<string>) => {
  const limit = ref(20);
  const offset = computed(() => {
    const p = page.value || 1;
    return (p > 1) ? ((p - 1) * limit.value) : 0;
  });

  return useLazyFetch<DataIndex>('/api/data', {
    query: {
      limit,
      offset,
      search,
    },
    deep: false,
  });
};

现在我希望将props以对象形式而非有序参数传入Composable,但这样会丢失props的响应式——即组件props变化时,查询不会重新执行。我尝试了以下方式但无效:

无效的组件代码:

<script setup lang="ts">
const props = defineProps({
  page: {
    default: 1,
    required: false,
    type: Number,
  },
  search: {
    required: true,
    type: String,
  },
});
const dataInput = reactive({
  page: props.page,
  search: props.search,
});
const { data, error, pending } = await useGetData(toRefs(dataInput));
</script>

对应的Composable代码:

interface GetDataInput {
  page?: Ref<number>
  search?: Ref<string>
};

export default (input: GetDataInput) => {
  const limit = ref(20);
  const offset = computed(() => {
    const p = input.page?.value || 1;
    return (p > 1) ? ((p - 1) * limit.value) : 0;
  });

  return useLazyFetch<DataIndex>('/api/data', {
    query: {
      limit,
      offset,
      search: input.search,
    },
    deep: false,
  });
};

我猜测丢失响应式的原因是dataInput未感知到props变化,或是传入Composable的input参数本身不响应式,请问我忽略了什么?


原因分析与解决方案

核心问题

你丢失响应式的根本原因是:

  • const dataInput = reactive({ page: props.page, search: props.search }) 这一步是将props的当前值赋值给dataInput的属性,而非建立响应式关联。props本身是响应式对象,但直接访问props.page会获取其原始值(非响应式引用),因此dataInput的属性不会随props变化而更新。
  • 后续的toRefs(dataInput)生成的ref仅与dataInput的属性绑定,和原props完全断开了响应式链接,自然无法感知props的变化。

解决方案一:直接对props使用toRefs

不需要中间的dataInput,直接将props转换为包含多个ref的对象传入Composable,这样每个ref都和原props保持响应式关联:

修改后的组件代码:

<script setup lang="ts">
const props = defineProps({
  page: {
    default: 1,
    required: false,
    type: Number,
  },
  search: {
    required: true,
    type: String,
  },
});

// 直接对props执行toRefs,生成与原props绑定的ref集合
const { data, error, pending } = await useGetData(toRefs(props));
</script>

Composable代码可以保持不变,因为toRefs(props)返回的结构完全符合GetDataInput接口的定义。

解决方案二:保持dataInput与props的响应式关联

如果确实需要通过dataInput加工props属性,要给dataInput的属性赋值为关联props的ref,而非直接赋值props的当前值:

修改后的组件代码:

<script setup lang="ts">
const props = defineProps({
  page: {
    default: 1,
    required: false,
    type: Number,
  },
  search: {
    required: true,
    type: String,
  },
});

const dataInput = reactive({
  // 用toRef将props的属性转换为响应式ref,绑定到dataInput
  page: toRef(props, 'page'),
  search: toRef(props, 'search'),
});

// 直接传入dataInput即可,无需toRefs
const { data, error, pending } = await useGetData(dataInput);
</script>

这种方式下,dataInput的属性会随props同步更新,传入Composable的ref自然能保持响应式。


内容的提问来源于stack exchange,提问作者broox

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:43:25