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
相关产品推荐
相关产品推荐

