如何将Firestore/Firebase Auth与Angular的NGXS状态管理器集成?
如何将Firestore/Firebase Auth与NGXS集成(Angular)
作为NGXS和Firebase的新手,我完全理解你找不到集成资料的困惑——毕竟这俩的组合文档确实不算多。下面我就一步步带你实现Firebase Auth和Firestore与NGXS的集成,都是实际项目里能用的方案:
前置准备
首先确保你的Angular项目已经配置好Firebase,同时安装必要的依赖包:
npm install @angular/fire firebase @ngxs/store @ngxs/logger-plugin @ngxs/devtools-plugin
注:logger和devtools插件是可选的,用于调试状态变化,开发阶段很实用
接下来在environment.ts里填入你的Firebase项目配置:
export const environment = { production: false, firebase: { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_SENDER_ID", appId: "YOUR_APP_ID" } };
Firebase Auth 集成步骤
我们先从用户认证开始,核心思路是把Firebase Auth的状态同步到NGXS的state中,并通过actions触发登录、登出等操作。
1. 创建Auth State文件
新建auth.state.ts,先定义状态模型和操作actions:
// 状态模型,保存用户信息、加载状态和错误信息 export interface AuthStateModel { user: firebase.User | null; loading: boolean; error: string | null; } // 定义触发状态变化的Actions export class Login { static readonly type = '[Auth] 用户登录'; constructor(public payload: { email: string; password: string }) {} } export class Logout { static readonly type = '[Auth] 用户登出'; } export class AuthStateChanged { static readonly type = '[Auth] 认证状态变更'; constructor(public payload: firebase.User | null) {} }
然后编写State类,处理Actions并与Firebase Auth交互:
import { State, Action, StateContext, Selector } from '@ngxs/store'; import { AuthStateModel, Login, Logout, AuthStateChanged } from './auth.state'; import { AngularFireAuth } from '@angular/fire/compat/auth'; @State<AuthStateModel>({ name: 'auth', // state的名称,用于在组件中选择状态 defaults: { user: null, loading: false, error: null } }) export class AuthState { constructor(private afAuth: AngularFireAuth) {} // 登录Action:调用Firebase登录API,更新加载状态和错误信息 @Action(Login) login(ctx: StateContext<AuthStateModel>, action: Login) { ctx.patchState({ loading: true, error: null }); return this.afAuth.signInWithEmailAndPassword(action.payload.email, action.payload.password) .catch(error => { ctx.patchState({ loading: false, error: error.message }); }); } // 登出Action:调用Firebase登出API,清空用户状态 @Action(Logout) logout(ctx: StateContext<AuthStateModel>) { return this.afAuth.signOut() .then(() => { ctx.patchState({ user: null }); }); } // 监听Auth状态变更:当Firebase的authState变化时,更新NGXS状态 @Action(AuthStateChanged) authStateChanged(ctx: StateContext<AuthStateModel>, action: AuthStateChanged) { ctx.patchState({ user: action.payload, loading: false }); } // 定义Selector,方便组件获取用户信息 @Selector() static getUser(state: AuthStateModel) { return state.user; } }
2. 初始化Auth状态监听
在app.component.ts的ngOnInit中监听Firebase Auth的状态变化,并同步到NGXS:
import { Component, OnInit } from '@angular/core'; import { AngularFireAuth } from '@angular/fire/compat/auth'; import { Store } from '@ngxs/store'; import { AuthStateChanged } from './auth.state'; @Component({ selector: 'app-root', templateUrl: './app.component.html' }) export class AppComponent implements OnInit { constructor(private afAuth: AngularFireAuth, private store: Store) {} ngOnInit() { // 监听Firebase Auth状态,一旦变化就触发NGXS的Action更新状态 this.afAuth.authState.subscribe(user => { this.store.dispatch(new AuthStateChanged(user)); }); } }
3. 在模块中注册State
最后在app.module.ts中导入必要的模块并注册AuthState:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AngularFireModule } from '@angular/fire/compat'; import { AngularFireAuthModule } from '@angular/fire/compat/auth'; import { NgxsModule } from '@ngxs/store'; import { NgxsLoggerPluginModule } from '@ngxs/logger-plugin'; import { NgxsDevtoolsPluginModule } from '@ngxs/devtools-plugin'; import { AppComponent } from './app.component'; import { AuthState } from './auth.state'; import { environment } from '../environments/environment'; @NgModule({ declarations: [AppComponent], imports: [ BrowserModule, AngularFireModule.initializeApp(environment.firebase), AngularFireAuthModule, NgxsModule.forRoot([AuthState]), NgxsLoggerPluginModule.forRoot(), // 开发阶段开启,可在控制台查看状态变化 NgxsDevtoolsPluginModule.forRoot() // 配合Redux Devtools使用 ], providers: [], bootstrap: [AppComponent] }) export class AppModule {}
Firestore 集成步骤
接下来是Firestore的集成,核心是利用Firestore的实时监听,让NGXS状态自动同步数据库的变化,同时通过Action触发增删改操作。
1. 创建Todo State示例(以Todo列表为例)
新建todo.state.ts,定义状态模型和Actions:
// Todo数据模型 export interface Todo { id: string; title: string; completed: boolean; } // Todo状态模型 export interface TodoStateModel { todos: Todo[]; loading: boolean; error: string | null; } // 定义Actions export class LoadTodos { static readonly type = '[Todo] 加载所有Todo'; } export class AddTodo { static readonly type = '[Todo] 添加新Todo'; constructor(public payload: Omit<Todo, 'id'>) {} }
然后编写State类,与Firestore交互:
import { State, Action, StateContext, Selector } from '@ngxs/store'; import { TodoStateModel, LoadTodos, AddTodo, Todo } from './todo.state'; import { AngularFirestore } from '@angular/fire/compat/firestore'; import { map, tap, catchError } from 'rxjs/operators'; import { throwError } from 'rxjs'; @State<TodoStateModel>({ name: 'todos', defaults: { todos: [], loading: false, error: null } }) export class TodoState { constructor(private afs: AngularFirestore) {} // 加载Todo列表:监听Firestore的实时变化,自动同步状态 @Action(LoadTodos) loadTodos(ctx: StateContext<TodoStateModel>) { ctx.patchState({ loading: true, error: null }); return this.afs.collection<Todo>('todos').snapshotChanges() .pipe( // 将Firestore的快照转换为我们需要的Todo格式 map(actions => actions.map(a => { const data = a.payload.doc.data() as Omit<Todo, 'id'>; const id = a.payload.doc.id; return { id, ...data } as Todo; })), // 更新NGXS状态 tap(todos => { ctx.patchState({ todos, loading: false }); }), // 处理错误 catchError(error => { ctx.patchState({ loading: false, error: error.message }); return throwError(() => error); }) ); } // 添加Todo:调用Firestore的add方法,由于已经监听了实时变化,状态会自动更新 @Action(AddTodo) addTodo(ctx: StateContext<TodoStateModel>, action: AddTodo) { return this.afs.collection<Todo>('todos').add(action.payload); } // 定义Selector,方便组件获取Todo列表 @Selector() static getTodos(state: TodoStateModel) { return state.todos; } }
2. 在模块中注册TodoState
更新app.module.ts,导入AngularFirestoreModule并注册TodoState:
// ... 其他导入 import { AngularFirestoreModule } from '@angular/fire/compat/firestore'; import { TodoState } from './todo.state'; @NgModule({ imports: [ // ... 其他模块 AngularFirestoreModule, NgxsModule.forRoot([AuthState, TodoState]) // 添加TodoState ] }) export class AppModule {}
一些实用的最佳实践
- 利用Selector简化组件状态获取:组件中通过
this.store.select(AuthState.getUser)或this.store.select(TodoState.getTodos)来获取状态,无需直接操作state。 - 使用AuthGuard保护路由:基于NGXS的Auth状态创建路由守卫,比如:
import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; import { Store } from '@ngxs/store'; import { AuthState } from './auth.state'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { constructor(private store: Store, private router: Router) {} canActivate(): Observable<boolean> { return this.store.select(AuthState.getUser) .pipe( map(user => { if (!user) { this.router.navigate(['/login']); return false; } return true; }) ); } }
- 分离异步逻辑到Effects(可选):如果你的业务逻辑比较复杂,可以使用
@ngxs/effects插件将异步操作从State中分离出来,让State更专注于状态更新。 - 清理订阅:虽然NGXS会自动处理Action中返回的Observable订阅,但如果手动订阅了Firestore的流,记得在组件销毁时取消订阅,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Lagistos
相关产品推荐
相关产品推荐

