Angular中如何用RXJS与Signals获取Firestore数据且不使用Observables
不用Observables实现Firestore数据获取(基于Signals)
实时监听版本(与原代码效果一致)
原代码使用docData和collectionData实现实时数据监听,我们可以改用Firestore的onSnapshot回调结合Angular Signals来实现,完全避免使用Observables:
import { signal, computed, effect } from '@angular/core'; import { Firestore, doc, collection, query, orderBy, onSnapshot, DocumentReference, CollectionReference } from '@angular/fire/firestore'; // 定义你的类型 interface _User { // 你的用户字段 name?: string; // ...其他字段 } interface Post { created_at: Date; // 你的帖子字段 content?: string; // ...其他字段 } @Component({ // 组件配置 }) export class YourComponent { profile_user_uid = signal<string | null>(null); // 单独存储用户资料和帖子的信号 private userProfile = signal<_User | null>(null); private userPosts = signal<Post[] | null>(null); // 存储取消监听的函数,用于清理资源 private unsubscribeProfile = signal<(() => void) | null>(null); private unsubscribePosts = signal<(() => void) | null>(null); constructor(private firestore: Firestore) { // 监听uid变化,处理数据获取 effect(() => { const uid = this.profile_user_uid(); // 先取消之前的监听,避免内存泄漏 this.unsubscribeProfile()?.(); this.unsubscribePosts()?.(); this.unsubscribeProfile.set(null); this.unsubscribePosts.set(null); if (!uid) { // uid为空时重置数据 this.userProfile.set(null); this.userPosts.set(null); return; } // 实时监听用户文档 const profileRef = doc(this.firestore, 'users', uid) as DocumentReference<_User>; const profileUnsub = onSnapshot(profileRef, (docSnap) => { if (docSnap.exists()) { this.userProfile.set({ uid, ...docSnap.data() }); } else { this.userProfile.set(null); } }); this.unsubscribeProfile.set(profileUnsub); // 实时监听帖子集合 const postsQuery = query( collection(this.firestore, 'users', uid, 'posts') as CollectionReference<Post>, orderBy('created_at', 'desc') ); const postsUnsub = onSnapshot(postsQuery, (colSnap) => { const posts = colSnap.docs.map(doc => ({ id: doc.id, ...doc.data() })); this.userPosts.set(posts); }); this.unsubscribePosts.set(postsUnsub); }); } // 组合成与原代码结构一致的profile信号 profile = computed(() => ({ user_profile: this.userProfile(), user_posts: this.userPosts() })); }
一次性获取版本(非实时)
如果不需要实时监听数据变化,仅需一次性获取数据,可以改用Firestore的getDoc和getDocs(返回Promise),结合effect和async/await实现:
import { signal, computed, effect } from '@angular/core'; import { Firestore, doc, collection, query, orderBy, getDoc, getDocs, DocumentReference, CollectionReference } from '@angular/fire/firestore'; // 类型定义同上... @Component({ // 组件配置 }) export class YourComponent { profile_user_uid = signal<string | null>(null); private userProfile = signal<_User | null>(null); private userPosts = signal<Post[] | null>(null); constructor(private firestore: Firestore) { effect(async () => { const uid = this.profile_user_uid(); if (!uid) { this.userProfile.set(null); this.userPosts.set(null); return; } try { // 获取用户资料 const profileRef = doc(this.firestore, 'users', uid) as DocumentReference<_User>; const profileDoc = await getDoc(profileRef); if (profileDoc.exists()) { this.userProfile.set({ uid, ...profileDoc.data() }); } else { this.userProfile.set(null); } // 获取帖子 const postsQuery = query( collection(this.firestore, 'users', uid, 'posts') as CollectionReference<Post>, orderBy('created_at', 'desc') ); const postsSnap = await getDocs(postsQuery); const posts = postsSnap.docs.map(doc => ({ id: doc.id, ...doc.data() })); this.userPosts.set(posts); } catch (err) { console.error('获取数据失败:', err); this.userProfile.set(null); this.userPosts.set(null); } }); } profile = computed(() => ({ user_profile: this.userProfile(), user_posts: this.userPosts() })); }
方案说明
- 实时版本通过
onSnapshot监听Firestore数据变化,回调中更新对应Signals,完全脱离Observable依赖 - 使用
effect监听profile_user_uid的变化,自动触发数据获取逻辑,同时处理旧监听的清理,避免内存泄漏 - 最终通过
computed将两个独立信号组合成与原代码结构一致的profile信号,保持调用逻辑不变
内容的提问来源于stack exchange,提问作者syahiruddin
相关产品推荐
相关产品推荐

