Angular10项目Firebase snapshotChanges()重复执行问题求助
解决Angular 10中Firebase snapshotChanges重复执行问题
问题核心原因
- 重复订阅:如果组件多次初始化(比如路由复用、未正确清理订阅),会导致
snapshotChanges()被多次订阅,每次集合变更都会触发多轮执行。 - Firebase本地/服务器双触发:Firebase实时监听会触发两次回调——本地写入成功时触发一次(未同步到服务器),服务器确认变更后再触发一次,这就是你执行
save_data()时监听逻辑触发两次的原因。 - get()方案使用错误:
get()返回Observable,仅通过pipe()不会触发执行,必须配合subscribe()或转换为Promise后使用await。
解决方案
1. 修复snapshotChanges重复执行(保留监听能力)
通过RxJS操作符管理订阅生命周期,并过滤本地未确认的变更:
import { Component, OnInit, OnDestroy } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil, filter } from 'rxjs/operators'; import { AngularFirestore } from '@angular/fire/firestore'; @Component({ // 组件元数据 }) export class YourComponent implements OnInit, OnDestroy { private destroy$ = new Subject<void>(); current_layout_data: any[] = []; current_store_uid: string; // 已定义的当前店铺ID current_date = new Date(); constructor(private afs: AngularFirestore) {} ngOnInit(): void { this.afs.collection('/layouts', ref => ref.where('store_uid', '==', this.current_store_uid) ) .snapshotChanges() .pipe( // 只处理服务器确认的变更,过滤本地未同步的触发 filter(actions => actions.every(action => !action.payload.doc.metadata.hasPendingWrites)), // 组件销毁时自动取消订阅,避免内存泄漏和重复订阅 takeUntil(this.destroy$) ) .subscribe(data => { console.log("layout 111"); // 简化数据处理逻辑 this.current_layout_data = data .map(docAction => docAction.payload.doc.data()) .filter(item => { const startDate = item.start_date.toDate(); const endDate = item.end_date.toDate(); return startDate <= this.current_date && endDate > this.current_date; }); this.layoutData(); }); // 同时处理layout_form.valueChanges的订阅,避免重复触发 this.layout_form.valueChanges .pipe(takeUntil(this.destroy$)) .subscribe(formData => { // 表单逻辑处理 }); } ngOnDestroy(): void { // 触发所有订阅取消 this.destroy$.next(); this.destroy$.complete(); } async save_data(data) { await this.afs.doc(`/layouts/${this.current_template_id}`).update({ layout_json_saved: data.layout_json_temp }); } layoutData(): void { // 你的layoutData逻辑 } }
2. 修复get()单次获取方案
如果需要单次获取数据而非持续监听,正确写法如下:
// 方式1:使用subscribe console.log("Flow coming here"); this.afs.collection('/layouts', ref => ref.where('store_uid', '==', this.current_store_uid) ) .get() .pipe( map(querySnapshot => { console.log("Data received:-", querySnapshot); this.current_layout_data = querySnapshot.docs .map(doc => doc.data()) .filter(item => { const startDate = item.start_date.toDate(); const endDate = item.end_date.toDate(); return startDate <= this.current_date && endDate > this.current_date; }); this.layoutData(); return this.current_layout_data; }) ) .subscribe(); // 方式2:使用await + toPromise() async getLayoutData() { console.log("Flow coming here"); const querySnapshot = await this.afs.collection('/layouts', ref => ref.where('store_uid', '==', this.current_store_uid) ).get().toPromise(); console.log("Data received:-", querySnapshot); this.current_layout_data = querySnapshot.docs .map(doc => doc.data()) .filter(item => { const startDate = item.start_date.toDate(); const endDate = item.end_date.toDate(); return startDate <= this.current_date && endDate > this.current_date; }); this.layoutData(); }
内容的提问来源于stack exchange,提问作者Darshan Mulani
相关产品推荐
相关产品推荐

