Vue子组件获取Pinia/VueFire对象后丢失响应性的问题
Vue子组件通过ID获取Firestore数据丢失响应性问题
问题背景
父组件通过props传递itemId给子组件,子组件调用Pinia store的itemForId(id)从VueFire/Firestore集合中获取数据。但子组件中的数据丢失响应性:外部更新Firestore文档时,Pinia store的集合和父组件数据会更新,子组件的项却不会同步。
若父组件直接把通过itemForId获取的项传给子组件,响应性正常,但此方案无法满足后续编辑需求,因此需要修复子组件的响应性问题。
相关代码
Pinia Store(projectStore.js)
const projectInfo = useDocument(() => doc(db, projectPath)) const { data:projectItems, promise:itemsPromise } = useCollection(() => collection(db, itemsPath)) function itemForId(id) { return projectItems.value.find((item) => item.id === id) }
父组件(MyParent.vue)
<script setup> import { useProjectStore } from 'stores/projectStore' const projectStore = useProjectStore() const { projectInfo, projectItems } = storeToRefs(projectStore) </script> <template> <div>子组件接收ID(无响应性)</div> <template v-for="itemId in projectInfo.itemOrder" :key="itemId"> <IdChild :itemId="itemId" /> </template> <div>子组件接收直接传递的项(有响应性)</div> <template v-for="itemId in projectInfo.itemOrder" :key="itemId"> <ItemChild :item="projectStore.itemForId(itemId)" /> </template> <div>原始集合数据(有响应性)</div> <div>{{ projectItems }}</div> </template>
无响应性的子组件(IdChild.vue)
<script setup> import { useProjectStore } from 'stores/projectStore' const props = defineProps({ itemId: String }) const projectStore = useProjectStore() const { projectItems } = storeToRefs(projectStore) // 仅初始化时赋值,后续不会自动更新 const item = ref(projectStore.itemForId(props.itemId)) </script> <template> <div>{{ item.name }}</div> <!-- 无响应性 --> <!-- 此写法有响应性,但每个属性单独处理不合理 --> <div>{{ projectStore.itemForId(itemId).name }}</div> <div>原始集合数据(有响应性)</div> <div>{{ projectItems }}</div> </template>
有响应性但无法编辑的子组件(ItemChild.vue)
<script setup> import { useProjectStore } from 'stores/projectStore' const props = defineProps({ item: Object }) </script> <template> <div>{{ item.name }}</div> <!-- 有响应性 --> </template>
解决方案
核心原因
用ref直接赋值的方式只会在组件初始化时执行一次,不会追踪projectItems或itemId的变化,因此当数据源更新时,子组件的item不会同步更新。
修复代码
将子组件中的ref替换为computed,利用计算属性的响应式依赖追踪特性,自动在数据源变化时重新查找对应项:
修改IdChild.vue:
<script setup> import { useProjectStore } from 'stores/projectStore' import { computed } from 'vue' const props = defineProps({ itemId: String }) const projectStore = useProjectStore() // 计算属性会自动追踪projectItems和itemId的变化 const item = computed(() => projectStore.itemForId(props.itemId)) </script> <template> <div>{{ item.name }}</div> <!-- 现在具备响应性 --> </template>
编辑功能说明
修复后可以直接编辑item.value的属性(VueFire返回的文档是响应式Proxy对象),示例编辑逻辑:
// 在IdChild.vue中添加编辑方法 function updateItemName(newName) { if (item.value) { item.value.name = newName // 后续可调用store方法同步到Firestore,例如: // projectStore.syncItemToFirestore(item.value) } }
而之前的ItemChild无法编辑是因为Vue props是单向数据流,直接修改props对象不符合设计规范,通过子组件内部用computed获取项的方式,更适合后续的编辑操作。
内容的提问来源于stack exchange,提问作者Brad
相关产品推荐
相关产品推荐

