Vue3下拉组件插槽内容Props更新不触发监听问题求助
问题根源
你遇到的核心问题是:将插槽的静态VNode快照存储到了store中,导致插槽内容脱离了原组件(ComponentA)的响应式上下文。当ComponentA的id props更新时,store里保存的还是showPopup执行时的旧插槽实例,自然不会同步更新。
另外,useSlots()返回的是非响应式对象,仅在组件渲染阶段更新,所以直接监听slots不会触发回调。
解决方案
方案1:传递组件配置+动态Props(推荐)
放弃存储插槽实例,改为传递组件类型和动态Props,让Dropdown Manager直接渲染组件并绑定最新Props:
1. 修改ComponentA中的Dropdown调用
<!-- ComponentA模板 --> <Dropdown :options="computedOptions" /> <script setup> import { defineProps, computed } from 'vue'; import ChildComponent from './ChildComponent.vue'; const props = defineProps({ id: String }); const tmpoptions = ref([/* 你的初始选项 */]); // 计算属性动态生成包含组件配置的选项 const computedOptions = computed(() => { return tmpoptions.value.map(opt => ({ ...opt, component: ChildComponent, componentProps: { id: props.id } })); }); </script>
2. 简化Dropdown组件的showPopup逻辑
<script setup> import { defineProps } from 'vue'; import mainStore from '@/stores/mainStore'; const props = defineProps({ options: Array }); const showPopup = () => { mainStore.updateStateItem({ options: props.options.map(x => ({ ...x })) }); }; </script>
3. Dropdown Manager动态渲染组件
<template> <div v-for="item in displayItems" :key="item.key"> <component v-if="item.component" :is="item.component" v-bind="item.componentProps" ></component> <span v-else>{{ item.key }}</span> </div> </template> <script setup> import { ref, watch } from 'vue'; import mainStore from '@/stores/mainStore'; const displayItems = ref([]); watch(() => mainStore.stateItem, (val) => { displayItems.value = val.options; }, { deep: true }); </script>
方案2:使用响应式渲染函数(保留插槽模式)
如果必须使用插槽,可通过provide/inject传递响应式的插槽渲染函数,确保每次渲染都获取最新Props:
1. ComponentA中提供响应式插槽映射
<script setup> import { defineProps, ref, watch, provide, h } from 'vue'; import ChildComponent from './ChildComponent.vue'; const props = defineProps({ id: String }); const tmpoptions = ref([/* 你的初始选项 */]); const slotMap = ref({}); // 监听id和选项变化,更新渲染函数 watch([() => props.id, () => tmpoptions.value], () => { slotMap.value = tmpoptions.value.reduce((map, opt) => { map[opt._id] = () => h(ChildComponent, { id: props.id }); return map; }, {}); }, { immediate: true }); // 提供给Dropdown Manager使用 provide('dropdownSlotMap', slotMap); </script>
2. Dropdown组件仅传递选项标识
<script setup> import { defineProps } from 'vue'; import mainStore from '@/stores/mainStore'; const props = defineProps({ options: Array }); const showPopup = () => { mainStore.updateStateItem({ options: props.options.map(x => ({ ...x })) }); }; </script>
3. Dropdown Manager注入并调用渲染函数
<template> <div v-for="item in displayItems" :key="item.key"> <component v-if="item.renderSlot" :is="item.renderSlot()"></component> <span v-else>{{ item.key }}</span> </div> </template> <script setup> import { ref, watch, inject } from 'vue'; import mainStore from '@/stores/mainStore'; const slotMap = inject('dropdownSlotMap'); const displayItems = ref([]); watch(() => mainStore.stateItem, (val) => { displayItems.value = val.options.map(opt => ({ ...opt, renderSlot: slotMap.value[opt._id] })); }, { deep: true }); </script>
内容的提问来源于stack exchange,提问作者Deniz Yalçın
相关产品推荐
相关产品推荐

