使用 Firebase 在 Angular 中進行狀態管理
已發表: 2022-03-11狀態管理是開發 Web 應用程序時要考慮的一個非常重要的架構。
在本教程中,我們將介紹一種在使用 Firebase 作為其後端的 Angular 應用程序中管理狀態的簡單方法。
我們將討論一些概念,例如狀態、存儲和服務。 希望這將幫助您更好地掌握這些術語,並更好地理解其他狀態管理庫,例如 NgRx 和 NgXs。
我們將構建一個員工管理頁面,以涵蓋一些不同的狀態管理場景以及可以處理它們的方法。
Angular 中的組件、服務、Firestore 和狀態管理
在典型的 Angular 應用程序中,我們有組件和服務。 通常,組件將用作視圖模板。 服務將包含業務邏輯和/或與外部 API 或其他服務通信以完成操作或檢索數據。
組件通常會顯示數據並允許用戶與應用程序交互以執行操作。 執行此操作時,數據可能會更改,並且應用程序會通過更新視圖來反映這些更改。
Angular 的更改檢測引擎負責檢查綁定到視圖的組件中的值何時發生更改並相應地更新視圖。
隨著應用程序的增長,我們將開始擁有越來越多的組件和服務。 通常,了解數據如何變化並跟踪發生變化的位置可能會很棘手。
Angular 和 Firebase
當我們使用 Firebase 作為後端時,我們會獲得一個非常簡潔的 API,其中包含構建實時應用程序所需的大部分操作和功能。
@angular/fire
是官方的 Angular Firebase 庫。 它是 Firebase JavaScript SDK 庫之上的一個層,可簡化 Firebase SDK 在 Angular 應用中的使用。 它非常適合 Angular 的良好實踐,例如使用 Observables 從 Firebase 獲取數據並將其顯示到我們的組件。
商店和狀態
我們可以將“狀態”視為應用程序中任何給定時間點顯示的值。 商店只是該應用程序狀態的持有者。
狀態可以建模為單個普通對像或一系列對象,反映應用程序的值。
Angular/Firebase 示例應用
讓我們構建它:首先,我們將使用 Angular CLI 創建一個基本的應用程序腳手架,並將其與 Firebase 項目連接起來。
$ npm install -g @angular/cli $ ng new employees-admin` Would you like to add Angular routing? Yes Which stylesheet format would you like to use? SCSS $ cd employees-admin/ $ npm install bootstrap # We'll add Bootstrap for the UI
而且,在styles.scss
上:
// ... @import "~bootstrap/scss/bootstrap";
接下來,我們將安裝@angular/fire
:
npm install firebase @angular/fire
現在,我們將在 Firebase 控制台中創建一個 Firebase 項目。
然後我們準備創建一個 Firestore 數據庫。
對於本教程,我將從測試模式開始。 如果您計劃發佈到生產環境,您應該強制執行規則以禁止不適當的訪問。
轉到項目概述 → 項目設置,然後將 Firebase 網絡配置複製到您的本地environments/environment.ts
。
export const environment = { production: false, firebase: { apiKey: "<api-key>", authDomain: "<auth-domain>", databaseURL: "<database-url>", projectId: "<project-id>", storageBucket: "<storage-bucket>", messagingSenderId: "<messaging-sender-id>" } };
至此,我們的應用程序的基本腳手架就位。 如果我們ng serve
,我們將得到:
Firestore 和 Store 基類
我們將創建兩個通用抽像類,然後我們將對其進行類型化和擴展以構建我們的服務。
泛型允許您編寫沒有綁定類型的行為。 這增加了代碼的可重用性和靈活性。
通用 Firestore 服務
為了利用 TypeScript 泛型,我們要做的是為@angular/fire
firestore
服務創建一個基本的泛型包裝器。
讓我們創建app/core/services/firestore.service.ts
。
這是代碼:
import { Inject } from "@angular/core"; import { AngularFirestore, QueryFn } from "@angular/fire/firestore"; import { Observable } from "rxjs"; import { tap } from "rxjs/operators"; import { environment } from "src/environments/environment"; export abstract class FirestoreService<T> { protected abstract basePath: string; constructor( @Inject(AngularFirestore) protected firestore: AngularFirestore, ) { } doc$(id: string): Observable<T> { return this.firestore.doc<T>(`${this.basePath}/${id}`).valueChanges().pipe( tap(r => { if (!environment.production) { console.groupCollapsed(`Firestore Streaming [${this.basePath}] [doc$] ${id}`) console.log(r) console.groupEnd() } }), ); } collection$(queryFn?: QueryFn): Observable<T[]> { return this.firestore.collection<T>(`${this.basePath}`, queryFn).valueChanges().pipe( tap(r => { if (!environment.production) { console.groupCollapsed(`Firestore Streaming [${this.basePath}] [collection$]`) console.table(r) console.groupEnd() } }), ); } create(value: T) { const id = this.firestore.createId(); return this.collection.doc(id).set(Object.assign({}, { id }, value)).then(_ => { if (!environment.production) { console.groupCollapsed(`Firestore Service [${this.basePath}] [create]`) console.log('[Id]', id, value) console.groupEnd() } }) } delete(id: string) { return this.collection.doc(id).delete().then(_ => { if (!environment.production) { console.groupCollapsed(`Firestore Service [${this.basePath}] [delete]`) console.log('[Id]', id) console.groupEnd() } }) } private get collection() { return this.firestore.collection(`${this.basePath}`); } }
這個abstract class
將作為我們 Firestore 服務的通用包裝器。
這應該是我們應該注入AngularFirestore
的唯一地方。 這將最大限度地減少@angular/fire
庫更新時的影響。 此外,如果在某個時候我們想要更改庫,我們只需要更新這個類。
我添加了doc$
、 collection$
、 create
和delete
。 它們包裝@angular/fire
的方法並在 Firebase 流式傳輸數據時提供日誌記錄(這對於調試來說非常方便)以及在創建或刪除對象之後。
通用商店服務
我們的通用商店服務將使用 RxJS 的BehaviorSubject
構建。 BehaviorSubject
讓訂閱者在訂閱後立即獲得最後發出的值。 在我們的例子中,這很有幫助,因為我們可以在訂閱 store 時為所有組件使用初始值來開始 store。
store 將有兩種方法, patch
和set
。 (我們稍後會創建get
方法。)
讓我們創建app/core/services/store.service.ts
:
import { BehaviorSubject, Observable } from 'rxjs'; import { environment } from 'src/environments/environment'; export abstract class StoreService<T> { protected bs: BehaviorSubject<T>; state$: Observable<T>; state: T; previous: T; protected abstract store: string; constructor(initialValue: Partial<T>) { this.bs = new BehaviorSubject<T>(initialValue as T); this.state$ = this.bs.asObservable(); this.state = initialValue as T; this.state$.subscribe(s => { this.state = s }) } patch(newValue: Partial<T>, event: string = "Not specified") { this.previous = this.state const newState = Object.assign({}, this.state, newValue); if (!environment.production) { console.groupCollapsed(`[${this.store} store] [patch] [event: ${event}]`) console.log("change", newValue) console.log("prev", this.previous) console.log("next", newState) console.groupEnd() } this.bs.next(newState) } set(newValue: Partial<T>, event: string = "Not specified") { this.previous = this.state const newState = Object.assign({}, newValue) as T; if (!environment.production) { console.groupCollapsed(`[${this.store} store] [set] [event: ${event}]`) console.log("change", newValue) console.log("prev", this.previous) console.log("next", newState) console.groupEnd() } this.bs.next(newState) } }
作為一個泛型類,我們將推遲輸入,直到它被正確擴展。
構造函數將接收Partial<T>
類型的初始值。 這將允許我們僅將值應用於狀態的某些屬性。 構造函數還將訂閱內部BehaviorSubject
發射,並在每次更改後保持內部狀態更新。
patch()
將接收Partial<T>
類型的newValue
並將其與 store 的當前this.state
值合併。 最後,我們next()
newState
並將新狀態發送給所有的 store 訂閱者。
set()
的工作方式非常相似,只是它不會修補狀態值,而是將其設置為它收到的newValue
。
我們將在更改發生時記錄狀態的前一個和下一個值,這將有助於我們調試和輕鬆跟踪狀態更改。
把它們放在一起
好的,讓我們看看這一切的實際效果。 我們要做的是創建一個員工頁面,其中將包含一個員工列表,以及一個用於添加新員工的表單。
讓我們更新app.component.html
以添加一個簡單的導航欄:
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-3"> <span class="navbar-brand mb-0 h1">Angular + Firebase + State Management</span> <ul class="navbar-nav mr-auto"> <li class="nav-item" [routerLink]="['/employees']" routerLinkActive="active"> <a class="nav-link">Employees</a> </li> </ul> </nav> <router-outlet></router-outlet>
接下來,我們將創建一個核心模塊:
ng gm Core
在core/core.module.ts
中,我們將添加應用所需的模塊:
// ... import { AngularFireModule } from '@angular/fire' import { AngularFirestoreModule } from '@angular/fire/firestore' import { environment } from 'src/environments/environment'; import { ReactiveFormsModule } from '@angular/forms' @NgModule({ // ... imports: [ // ... AngularFireModule.initializeApp(environment.firebase), AngularFirestoreModule, ReactiveFormsModule, ], exports: [ CommonModule, AngularFireModule, AngularFirestoreModule, ReactiveFormsModule ] }) export class CoreModule { }
現在,讓我們創建員工頁面,從員工模塊開始:
ng gm Employees --routing
在employees-routing.module.ts
中,讓我們添加employees
路由:
// ... import { EmployeesPageComponent } from './components/employees-page/employees-page.component'; // ... const routes: Routes = [ { path: 'employees', component: EmployeesPageComponent } ]; // ...
在employees.module.ts
中,我們將導入ReactiveFormsModule
:
// ... import { ReactiveFormsModule } from '@angular/forms'; // ... @NgModule({ // ... imports: [ // ... ReactiveFormsModule ] }) export class EmployeesModule { }
現在,讓我們在app.module.ts
文件中添加這兩個模塊:
// ... import { EmployeesModule } from './employees/employees.module'; import { CoreModule } from './core/core.module'; imports: [ // ... CoreModule, EmployeesModule ],
最後,讓我們創建員工頁面的實際組件,以及相應的模型、服務、商店和狀態。
ng gc employees/components/EmployeesPage ng gc employees/components/EmployeesList ng gc employees/components/EmployeesForm
對於我們的模型,我們需要一個名為models/employee.ts
的文件:
export interface Employee { id: string; name: string; location: string; hasDriverLicense: boolean; }
我們的服務將存在於一個名為employees/services/employee.firestore.ts
的文件中。 該服務將擴展之前創建的通用FirestoreService<T>
,我們只需設置 Firestore 集合的basePath
:
import { Injectable } from '@angular/core'; import { FirestoreService } from 'src/app/core/services/firestore.service'; import { Employee } from '../models/employee'; @Injectable({ providedIn: 'root' }) export class EmployeeFirestore extends FirestoreService<Employee> { protected basePath: string = 'employees'; }
然後我們將創建文件employees/states/employees-page.ts
。 這將作為員工頁面的狀態:
import { Employee } from '../models/employee'; export interface EmployeesPage { loading: boolean; employees: Employee[]; formStatus: string; }
state 將有一個loading
值,它決定是否在頁面上顯示加載消息, employees
自己,以及一個formStatus
變量來處理表單的狀態(例如Saving
或Saved
。)
我們需要一個位於employees/services/employees-page.store.ts
的文件。 在這裡,我們將擴展之前創建的StoreService<T>
。 我們將設置商店名稱,用於在調試時識別它。
該服務將初始化並保存員工頁面的狀態。 請注意,構造函數使用頁面的初始狀態調用super()
。 在這種情況下,我們將使用loading=true
和一個空的員工數組來初始化狀態。
import { EmployeesPage } from '../states/employees-page'; import { StoreService } from 'src/app/core/services/store.service'; import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class EmployeesPageStore extends StoreService<EmployeesPage> { protected store: string = 'employees-page'; constructor() { super({ loading: true, employees: [], }) } }
現在讓我們創建EmployeesService
來整合EmployeeFirestore
和EmployeesPageStore
:
ng gs employees/services/Employees
請注意,我們在此服務中註入了EmployeeFirestore
和EmployeesPageStore
。 這意味著EmployeesService
將包含並協調對Firestore 和商店的調用以更新狀態。 這將幫助我們創建一個供組件調用的 API。
import { EmployeesPageStore } from './employees-page.store'; import { EmployeeFirestore } from './employee.firestore'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { Employee } from '../models/employee'; import { tap, map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class EmployeesService { constructor( private firestore: EmployeeFirestore, private store: EmployeesPageStore ) { this.firestore.collection$().pipe( tap(employees => { this.store.patch({ loading: false, employees, }, `employees collection subscription`) }) ).subscribe() } get employees$(): Observable<Employee[]> { return this.store.state$.pipe(map(state => state.loading ? [] : state.employees)) } get loading$(): Observable<boolean> { return this.store.state$.pipe(map(state => state.loading)) } get noResults$(): Observable<boolean> { return this.store.state$.pipe( map(state => { return !state.loading && state.employees && state.employees.length === 0 }) ) } get formStatus$(): Observable<string> { return this.store.state$.pipe(map(state => state.formStatus)) } create(employee: Employee) { this.store.patch({ loading: true, employees: [], formStatus: 'Saving...' }, "employee create") return this.firestore.create(employee).then(_ => { this.store.patch({ formStatus: 'Saved!' }, "employee create SUCCESS") setTimeout(() => this.store.patch({ formStatus: '' }, "employee create timeout reset formStatus"), 2000) }).catch(err => { this.store.patch({ loading: false, formStatus: 'An error ocurred' }, "employee create ERROR") }) } delete(id: string): any { this.store.patch({ loading: true, employees: [] }, "employee delete") return this.firestore.delete(id).catch(err => { this.store.patch({ loading: false, formStatus: 'An error ocurred' }, "employee delete ERROR") }) } }
讓我們看一下該服務將如何工作。

在構造函數中,我們將訂閱 Firestore 員工集合。 一旦 Firestore 從集合中發出數據,我們將更新存儲,設置loading=false
並使用 Firestore 返回的集合來設置employees
。 由於我們注入了EmployeeFirestore
,因此從 Firestore 返回的對像類型為Employee
,從而啟用了更多 IntelliSense 功能。
此訂閱將在應用程序處於活動狀態時處於活動狀態,並在每次 Firestore 流式傳輸數據時偵聽所有更改並更新商店。
this.firestore.collection$().pipe( tap(employees => { this.store.patch({ loading: false, employees, }, `employees collection subscription`) }) ).subscribe()
employees$()
和loading$()
函數將選擇我們稍後要在組件上使用的狀態。 employees$()
將在狀態加載時返回一個空數組。 這將允許我們在視圖上顯示正確的消息。
get employees$(): Observable<Employee[]> { return this.store.state$.pipe(map(state => state.loading ? [] : state.employees)) } get loading$(): Observable<boolean> { return this.store.state$.pipe(map(state => state.loading)) }
好的,現在我們已經準備好所有的服務,我們可以構建我們的視圖組件了。 但在我們這樣做之前,快速復習可能會派上用場……
RxJs Observables 和async
管道
Observables 允許訂閱者以流的形式接收數據的發射。 這與async
管道相結合,可以非常強大。
async
管道負責訂閱 Observable 並在發出新數據時更新視圖。 更重要的是,它會在組件被銷毀時自動取消訂閱,從而保護我們免受內存洩漏。
您可以在官方文檔中閱讀有關 Observables 和 RxJs 庫的更多信息。
創建視圖組件
在employees/components/employees-page/employees-page.component.html
中,我們將放置以下代碼:
<div class="container"> <div class="row"> <div class="col-12 mb-3"> <h4> Employees </h4> </div> </div> <div class="row"> <div class="col-6"> <app-employees-list></app-employees-list> </div> <div class="col-6"> <app-employees-form></app-employees-form> </div> </div> </div>
同樣, employees/components/employees-list/employees-list.component.html
也會有這個,使用上面提到的async
管道技術:
<div *ngIf="loading$ | async"> Loading... </div> <div *ngIf="noResults$ | async"> No results </div> <div class="card bg-light mb-3" *ngFor="let employee of employees$ | async"> <div class="card-header">{{employee.location}}</div> <div class="card-body"> <h5 class="card-title">{{employee.name}}</h5> <p class="card-text">{{employee.hasDriverLicense ? 'Can drive': ''}}</p> <button (click)="delete(employee)" class="btn btn-danger">Delete</button> </div> </div>
但在這種情況下,我們也需要一些用於組件的 TypeScript 代碼。 文件employees/components/employees-list/employees-list.component.ts
將需要這個:
import { Employee } from '../../models/employee'; import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { EmployeesService } from '../../services/employees.service'; @Component({ selector: 'app-employees-list', templateUrl: './employees-list.component.html', styleUrls: ['./employees-list.component.scss'] }) export class EmployeesListComponent implements OnInit { loading$: Observable<boolean>; employees$: Observable<Employee[]>; noResults$: Observable<boolean>; constructor( private employees: EmployeesService ) {} ngOnInit() { this.loading$ = this.employees.loading$; this.noResults$ = this.employees.noResults$; this.employees$ = this.employees.employees$; } delete(employee: Employee) { this.employees.delete(employee.id); } }
因此,轉到瀏覽器,我們現在將擁有的是:
控制台將有以下輸出:
看看這個,我們可以看出 Firestore 使用空值流式傳輸employees
集合,並且employees-page
存儲已修補,將loading
從true
設置為false
。
好的,讓我們構建將新員工添加到 Firestore 的表單:
員工表格
在employees/components/employees-form/employees-form.component.html
中,我們將添加以下代碼:
<form [formGroup]="form" (ngSubmit)="submit()"> <div class="form-group"> <label for="name">Name</label> <input type="string" class="form-control" formControlName="name" [class.is-invalid]="isInvalid('name')"> <div class="invalid-feedback"> Please enter a Name. </div> </div> <div class="form-group"> <select class="custom-select" formControlName="location" [class.is-invalid]="isInvalid('location')"> <option value="" selected>Choose location</option> <option *ngFor="let loc of locations" [ngValue]="loc">{{loc}}</option> </select> <div class="invalid-feedback"> Please select a Location. </div> </div> <div class="form-group form-check"> <input type="checkbox" class="form-check-input" formControlName="hasDriverLicense"> <label class="form-check-label" for="hasDriverLicense">Has driver license</label> </div> <button [disabled]="form.invalid" type="submit" class="btn btn-primary d-inline">Add</button> <span class="ml-2">{{ status$ | async }}</span> </form>
相應的 TypeScript 代碼將存在於employees/components/employees-form/employees-form.component.ts
:
import { EmployeesService } from './../../services/employees.service'; import { AngularFirestore } from '@angular/fire/firestore'; import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl, Validators } from '@angular/forms'; import { Observable } from 'rxjs'; @Component({ selector: 'app-employees-form', templateUrl: './employees-form.component.html', styleUrls: ['./employees-form.component.scss'] }) export class EmployeesFormComponent implements OnInit { form: FormGroup = new FormGroup({ name: new FormControl('', Validators.required), location: new FormControl('', Validators.required), hasDriverLicense: new FormControl(false) }); locations = [ 'Rosario', 'Buenos Aires', 'Bariloche' ] status$: Observable < string > ; constructor( private employees: EmployeesService ) {} ngOnInit() { this.status$ = this.employees.formStatus$; } isInvalid(name) { return this.form.controls[name].invalid && (this.form.controls[name].dirty || this.form.controls[name].touched) } async submit() { this.form.disable() await this.employees.create({ ...this.form.value }) this.form.reset() this.form.enable() } }
該表單將調用EmployeesService
的create()
方法。 現在頁面看起來是這樣的:
讓我們看看當我們添加一個新員工時會發生什麼。
添加新員工
添加新員工後,我們將在控制台看到以下獲取輸出:
這些都是添加新員工時觸發的所有事件。 讓我們仔細看看。
當我們調用create()
時,我們將執行以下代碼,將loading=true
、 formStatus='Saving...'
和employees
數組設置為空(上圖中的(1)
)。
this.store.patch({ loading: true, employees: [], formStatus: 'Saving...' }, "employee create") return this.firestore.create(employee).then(_ => { this.store.patch({ formStatus: 'Saved!' }, "employee create SUCCESS") setTimeout(() => this.store.patch({ formStatus: '' }, "employee create timeout reset formStatus"), 2000) }).catch(err => { this.store.patch({ loading: false, formStatus: 'An error ocurred' }, "employee create ERROR") })
接下來,我們調用基本 Firestore 服務來創建員工,該員工記錄(4)
。 在 promise 回調中,我們設置formStatus='Saved!'
和日誌(5)
。 最後,我們設置了一個超時時間,將formStatus
設置回空,記錄(6)
。
日誌事件(2)
和(3)
是 Firestore 訂閱員工集合觸發的事件。 當EmployeesService
被實例化時,我們訂閱該集合併在每次發生更改時接收該集合。
通過將employees
數組設置為來自Firestore 的員工,這將為store 設置一個新狀態, loading=false
。
如果我們展開日誌組,我們將看到每個事件和存儲更新的詳細數據,包括前一個值和下一個值,這對於調試很有用。
這是添加新員工後頁面的樣子:
添加摘要組件
假設我們現在想在我們的頁面上顯示一些摘要數據。 假設我們想要員工總數,有多少是司機,有多少來自羅薩里奧。
我們首先將新的狀態屬性添加到employees/states/employees-page.ts
中的頁面狀態模型中:
// ... export interface EmployeesPage { loading: boolean; employees: Employee[]; formStatus: string; totalEmployees: number; totalDrivers: number; totalRosarioEmployees: number; }
我們將在employees/services/emplyees-page.store.ts
的 store 中初始化它們:
// ... constructor() { super({ loading: true, employees: [], totalDrivers: 0, totalEmployees: 0, totalRosarioEmployees: 0 }) } // ...
接下來,我們將計算新屬性的值並將它們各自的選擇器添加到EmployeesService
中:
// ... this.firestore.collection$().pipe( tap(employees => { this.store.patch({ loading: false, employees, totalEmployees: employees.length, totalDrivers: employees.filter(employee => employee.hasDriverLicense).length, totalRosarioEmployees: employees.filter(employee => employee.location === 'Rosario').length, }, `employees collection subscription`) }) ).subscribe() // ... get totalEmployees$(): Observable < number > { return this.store.state$.pipe(map(state => state.totalEmployees)) } get totalDrivers$(): Observable < number > { return this.store.state$.pipe(map(state => state.totalDrivers)) } get totalRosarioEmployees$(): Observable < number > { return this.store.state$.pipe(map(state => state.totalRosarioEmployees)) } // ...
現在,讓我們創建摘要組件:
ng gc employees/components/EmployeesSummary
我們將把它放在employees/components/employees-summary/employees-summary.html
:
<p> <span class="font-weight-bold">Total:</span> {{total$ | async}} <br> <span class="font-weight-bold">Drivers:</span> {{drivers$ | async}} <br> <span class="font-weight-bold">Rosario:</span> {{rosario$ | async}} <br> </p>
在employees/components/employees-summary/employees-summary.ts
:
import { Component, OnInit } from '@angular/core'; import { EmployeesService } from '../../services/employees.service'; import { Observable } from 'rxjs'; @Component({ selector: 'app-employees-summary', templateUrl: './employees-summary.component.html', styleUrls: ['./employees-summary.component.scss'] }) export class EmployeesSummaryComponent implements OnInit { total$: Observable < number > ; drivers$: Observable < number > ; rosario$: Observable < number > ; constructor( private employees: EmployeesService ) {} ngOnInit() { this.total$ = this.employees.totalEmployees$; this.drivers$ = this.employees.totalDrivers$; this.rosario$ = this.employees.totalRosarioEmployees$; } }
然後我們將該組件添加到employees/employees-page/employees-page.component.html
:
// ... <div class="col-12 mb-3"> <h4> Employees </h4> <app-employees-summary></app-employees-summary> </div> // ...
結果如下:
在控制台中,我們有:
員工服務計算每次排放的總totalEmployees
、 totalDrivers
和totalRosarioEmployees
並更新狀態。
本教程的完整代碼可在 GitHub 上找到,並且還有一個現場演示。
使用 Observables 管理 Angular 應用程序狀態……檢查!
在本教程中,我們介紹了一種使用 Firebase 後端管理 Angular 應用程序狀態的簡單方法。
這種方法非常適合使用 Observables 的 Angular 指南。 它還通過跟踪應用程序狀態的所有更新來促進調試。
通用商店服務還可用於管理不使用 Firebase 功能的應用的狀態,或者僅管理應用的數據或來自其他 API 的數據。
但是在你不加選擇地應用它之前,需要考慮的一件事是, EmployeesService
在構造函數上訂閱了 Firestore,並在應用程序處於活動狀態時繼續監聽。 如果我們在應用程序的多個頁面上使用員工列表,這可能很有用,以避免在頁面之間導航時從 Firestore 獲取數據。
但這在其他情況下可能不是最佳選擇,例如您只需提取一次初始值,然後手動觸發從 Firebase 重新加載數據。 最重要的是,了解您的應用程序的要求以選擇更好的實施方法始終很重要。