Firestore onSnapshot()函数被触发两次的技术咨询
Firestore onSnapshot() 被调用两次的解决方案
遇到这个问题很常见,我来帮你拆解下可能的原因和对应的解决办法:
1. 本地缓存 + 服务器同步的默认行为
Firestore 的 onSnapshot() 有个默认机制:它会先返回本地缓存中的文档数据(如果之前加载过),等和服务器同步完成后,再返回服务器端的最新数据,这就会触发两次回调。这不是bug,是Firestore为了提升用户体验设计的特性。
解决办法:通过metadata区分来源
你可以通过快照的 metadata.fromCache 属性来判断数据是来自本地缓存还是服务器,按需处理:
let user = firebase.firestore().collection('users').doc(userID).onSnapshot({ next: (documentSnapshot: firebase.firestore.DocumentSnapshot) => { // 只处理服务器返回的最终数据 if (!documentSnapshot.metadata.fromCache) { this.userArray.push(documentSnapshot as User); console.log(documentSnapshot); } // 如果需要用到本地缓存数据,可以在这里加else逻辑 // else { // console.log("本地缓存数据,用于快速展示", documentSnapshot); // } }, error: (firestoreError: firebase.firestore.FirestoreError) => { console.log(firestoreError); } });
2. 重复订阅未取消(前端框架场景)
如果你是在Vue、React、Angular这类框架中使用,组件重复挂载(比如路由切换后返回)会导致多次创建 onSnapshot 监听,看起来像是同一个回调触发了两次。
解决办法:组件卸载时取消订阅
onSnapshot() 会返回一个取消订阅的函数,记得在组件销毁时调用它:
// 先保存取消订阅的函数引用 private unsubscribeUserListener!: () => void; // 在组件初始化时订阅 initUserListener() { this.unsubscribeUserListener = firebase.firestore().collection('users').doc(userID).onSnapshot({ next: (doc) => { this.userArray.push(doc as User); console.log(doc); }, error: (err) => console.log(err) }); } // 组件销毁时取消订阅(以Angular为例,React用useEffect的清理函数,Vue用onUnmounted) ngOnDestroy() { if (this.unsubscribeUserListener) { this.unsubscribeUserListener(); } }
3. 文档内容确实发生了更新
如果在订阅后,该用户文档刚好有数据更新,onSnapshot() 也会触发两次:第一次是初始数据,第二次是更新后的数据。
解决办法:对比数据变化再处理
你可以保存上一次的数据,只在内容变化时执行逻辑:
let lastUserData: User | null = null; let user = firebase.firestore().collection('users').doc(userID).onSnapshot({ next: (documentSnapshot: firebase.firestore.DocumentSnapshot) => { const currentUser = documentSnapshot.data() as User; // 对比数据是否变化(简单对比可以用JSON.stringify,复杂对象建议用深比较库) if (JSON.stringify(currentUser) !== JSON.stringify(lastUserData)) { this.userArray.push(currentUser); console.log(documentSnapshot); lastUserData = currentUser; } }, error: (firestoreError: firebase.firestore.FirestoreError) => { console.log(firestoreError); } });
先排查第一种情况,这是最常见的原因,用metadata判断就能快速解决~
内容的提问来源于stack exchange,提问作者BOOnZ
相关产品推荐
相关产品推荐

