如何通过Vue的onInvalidate回调取消Firestore流订阅?
问题原因
你的onInvalidate回调未触发有两个核心原因:
- 你在
watchEffect内部没有访问任何响应式变量,watchEffect仅会在依赖的响应式变量更新、自身被手动停止、所属组件卸载这三种场景下触发清理回调。因为没有响应式依赖,所以不会因为更新触发重新执行,自然不会运行清理逻辑。 - 如果你使用
useCollection的组件没有被卸载,也没有手动停止watchEffect,清理回调自然不会执行。
验证订阅取消的方法
- 功能验证:主动触发取消订阅逻辑后,修改Firestore对应集合的数据,观察本地
documents数组是否还会同步更新,没有更新即说明订阅已取消。 - 网络验证:打开浏览器开发者工具的「网络」面板,筛选「WebSocket」类型的请求,找到Firestore的实时连接,取消订阅后会看到对应监听的关闭帧,无后续数据推送即说明取消成功。
- 用量验证:可以在Firebase控制台的「使用和账单」→「用量详情」页面查看Firestore的读取次数,取消订阅后无额外读取操作的话,计数不会持续上涨,可侧面验证。
注意:Firebase控制台没有直接的实时订阅活跃状态查询入口,无法直接查看单条订阅的状态
代码优化方案
你的场景下用onUnmounted钩子处理取消订阅比无依赖的watchEffect更合适,同时你原有代码存在快照数据重复累加的问题,修改后的参考代码:
import { ref, reactive, onUnmounted } from "vue"; import { projectFirestore } from "@/firebase/config"; import { query, orderBy, onSnapshot, DocumentData, collection, } from "firebase/firestore"; const useCollection = (col: string) => { const documents = reactive<Record<string, unknown>[]>([]); const error = ref<string | null>(null); const collectionReference = collection(projectFirestore, col); const collectionOrdered = query( collectionReference, orderBy("createdAt", "desc") ); const unsubscribe = onSnapshot( collectionOrdered, (snapshot) => { // 先清空原有数据避免重复累加 documents.splice(0); snapshot.docs.forEach((doc: DocumentData) => { documents.push({ ...doc.data(), id: doc.id }); }); error.value = null; }, (err) => { console.log(err.message); documents.splice(0); error.value = err.message; } ); // 组件卸载时自动取消订阅 onUnmounted(() => { unsubscribe(); console.log("unsubscribe fired"); }); // 若需要手动取消订阅,可将unsubscribe返回 return { documents, error, unsubscribe }; }; export default useCollection;
内容的提问来源于stack exchange,提问作者TinyTiger
相关产品推荐
相关产品推荐

