作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
华金·希德的头像

Joaquin Cid

Joaquin是一名全栈和混合移动应用程序开发人员,在WebMD和Getty Images等公司工作了超过12年的经验.

Previously At

Velocity Partners
Share

状态管理是开发web应用程序时需要考虑的一个非常重要的架构部分.

在本教程中,我们将介绍一种管理状态的简单方法 角的应用程序 that uses Firebase as its back end.

我们将讨论一些概念,如状态、存储和服务. 希望这将帮助您更好地掌握这些术语,并更好地理解其他术语 状态管理库 such as NgRx and NgXs.

我们将构建一个员工管理页面,以介绍一些不同的状态管理场景和可以处理它们的方法.

Angular中的组件、服务、Firestore和状态管理

On a typical Angular 我们有组件和服务. 通常,组件将充当视图模板. 服务将包含业务逻辑和/或与外部api或其他服务通信,以完成操作或检索数据.

组件连接到服务,服务连接到其他服务或HTTP api.

组件通常会显示数据,并允许用户与应用程序交互以执行操作. 在此过程中,数据可能会发生变化,应用程序会通过更新视图来反映这些变化.

Angular的变更检测引擎负责检查绑定到视图的组件中的值何时发生了变化,并相应地更新视图.

随着应用程序的发展,我们将开始拥有越来越多的组件和服务. 通常,理解数据是如何变化的,并跟踪发生变化的位置可能很棘手.

Angular和Firebase

When we use Firebase as our back end, 我们提供了一个非常简洁的API,它包含了构建实时应用程序所需的大部分操作和功能.

@angular/fire 是官方的Angular Firebase库吗. 它是Firebase JavaScript SDK库之上的一层,简化了Firebase SDK在Angular应用中的使用. 它很好地契合了Angular的良好实践,比如使用Observables从Firebase获取数据并将其显示给我们的组件.

组件通过@angular/fire使用observable订阅Firebase JavaScript API.

Stores and State

我们可以把“状态”看作是在应用程序中任何给定时间点显示的值. 存储只是该应用程序状态的持有者.

状态可以建模为单个普通对象或一系列对象, 反映应用程序的价值.

存储保持状态, 其中有一个示例对象与一些简单的键值对名称, city, and country.

Angular/Firebase示例应用

首先,让我们构建它, 我们将使用Angular CLI创建一个基本的应用脚手架, 并将其与Firebase项目连接.

$ NPM install -g @angular/cli
$ ng new employees-admin '

你想添加Angular路由吗? Yes
您希望使用哪种样式表格式? SCSS

$ CD employees-admin/
我们将为UI添加bootstrap

And, on styles.scss:

// ...
@ import " ~引导/ scss引导”;

接下来,我们将进行安装 @angular/fire:

NPM安装firebase @angular/fire

现在,我们将创建一个Firebase项目 Firebase控制台.

The Firebase console's "Add a project" dialog.

然后我们就可以创建一个Firestore数据库了.

在本教程中,我将从测试模式开始. 如果您计划将其发布到生产环境,则应该强制执行规则以禁止不适当的访问.

“Cloud Firestore的安全规则”对话框, 选择“在测试模式启动”而不是“在锁定模式启动”."

转到项目概述→项目设置,并将Firebase web配置复制到本地 环境/环境.ts.

新Firebase项目的空应用程序列表.

导出const环境= {
    生产:假的,
        firebase: {
        apiKey: "",
        authDomain: "",
        databaseURL: "",
        projectId: "",
        storageBucket: "",
        messagingSenderId: ""
    }
};

至此,我们已经为应用程序准备好了基本的脚手架. If we ng serve, we’ll get:

Angular脚手架,上面写着“欢迎来到employees-admin”!"

Firestore和Store基类

我们将创建两个泛型抽象类, 然后我们将输入并从中扩展以构建我们的服务.

Generics 允许您编写没有绑定类型的行为. This 增加了可重用性和灵活性 to your code.

通用消防站服务

为了利用TypeScript的泛型, 我们要做的是为 @angular/fire firestore service.

Let’s create 应用程序/核心/服务/ firestore.service.ts.

Here’s the code:

从“@angular/core”中导入{Inject};
从“@angular/fire/firestore”中导入{AngularFirestore, QueryFn};
从“rxjs”中导入{Observable};
从“rxjs/operators”中导入{tap};
从"src/环境/环境"中导入{environment};

    export abstract class FirestoreService {

    保护抽象basePath:字符串;

    constructor(
        @Inject(AngularFirestore) protected firestore:
    ) {

    }

    doc$(id: string): Observable {
        return this.firestore.doc(`${this.basePath}/${id}`).valueChanges().pipe(
            tap(r => {
                if (!environment.production) {
                    console.groupcollapse (' Firestore Streaming [${this.basePath}] [doc$] ${id} ')
                    console.log(r)
                    console.groupEnd()
                }
            }),
        );
    }

    $ (queryFn集合?: QueryFn): Observable {
        return this.firestore.collection(`${this.basePath} ', queryFn).valueChanges().pipe(
            tap(r => {
                if (!environment.production) {
                    console.groupcollapse (' Firestore Streaming [${this.basePath}][收集$]”)
                    console.table(r)
                    console.groupEnd()
                }
            }),
        );
    }

    create(value: T) {
        const id = this.firestore.createId();
        return this.collection.doc(id).set(Object.赋值({},{id}, value)).then(_ => {
            if (!environment.production) {
                console.groupcollapse (' Firestore服务[${这个.basePath}][制造]”)
                console.log('[Id]', Id, value)
                console.groupEnd()
            }
        })
    }

    删除(id: string) {
        return this.collection.doc(id).delete().then(_ => {
            if (!environment.production) {
                console.groupcollapse (' Firestore服务[${这个.basePath}][删除]”)
                console.log('[Id]', id)
                console.groupEnd()
            }
        })
    }

    Private get collection() {
        return this.firestore.(“${这个集合.basePath}`);
    }
}

This abstract class 将作为我们Firestore服务的通用包装器.

这应该是我们唯一应该注射的地方 AngularFirestore. 这将最大限度地减少影响,当 @angular/fire 图书馆得到更新. 同样,如果在某个时候我们想要更改库,我们只需要更新这个类.

I added doc$, collection$, create, and delete. They wrap @angular/fire并在Firebase流数据时(这将非常便于调试)以及对象创建或删除后提供日志记录.

通用存储服务

我们的通用存储服务将使用RxJS构建。 BehaviorSubject. BehaviorSubject 让订阅者在订阅后立即获得最后发出的值. In our case, 这很有帮助,因为我们将能够在所有组件订阅该存储时使用初始值开始该存储.

该存储将有两种方法, patch and set. (We’ll create get methods later.)

Let’s create 应用程序/核心/服务/商店.service.ts:

从rxjs中导入{BehaviorSubject, Observable};
从“src/环境/环境”中导入{environment};

export abstract class StoreService {

    protected bs: BehaviorSubject;
    state$: Observable;
    state: T;
    previous: T;

    受保护的抽象存储:string;

    constructor(initialValue: Partial) {
        this.bs = new BehaviorSubject(initialValue as T);
        this.state$ = this.bs.asObservable();

        this.state = initialValue = T;
        this.state$.subscribe(s => {
            this.state = s
        })
    }

    patch(newValue: Partial, event: string = "Not specified") {
        this.previous = this.state
        const newState =对象.assign({}, this.state, newValue);
        if (!environment.production) {
            console.groupCollapsed(“[$ {.[补丁][事件:${事件}]')
            console.日志(“改变”,newValue)
            console.log("prev", this.previous)
            console.日志(“下一个”,newState)
            console.groupEnd()
        }
        this.bs.next(newState)
    }

    set(newValue: Partial, event: string = "Not specified") {
        this.previous = this.state
        const newState =对象.assign({}, newValue)为T;
        if (!environment.production) {
            console.groupCollapsed(“[$ {.Store} Store] [set] [event: ${event}] ')
            console.日志(“改变”,newValue)
            console.log("prev", this.previous)
            console.日志(“下一个”,newState)
            console.groupEnd()
        }
        this.bs.next(newState)
    }
}

作为泛型类,我们将延迟键入,直到它得到适当扩展.

构造函数将接收type的初始值 Partial. 这将允许我们只对状态的某些属性应用值. 构造函数也将订阅内部的 BehaviorSubject 并在每次更改后保持内部状态更新.

patch() will receive the newValue of type Partial 然后和电流合并 this.state 商店的价值. Finally, we next() the newState 并向所有商店订阅者发出新状态.

set() 工作原理非常相似,只是它不会修补状态值,而是将其设置为 newValue it received.

当发生更改时,我们将记录状态的上一个和下一个值, 这将帮助我们调试和轻松地跟踪状态变化.

把它们放在一起

好了,让我们来看看实际情况. 我们要做的是创建一个员工页面, 其中将包含员工列表, 加上一个添加新员工的表单.

Let’s update app.component.html 添加一个简单的导航栏:



接下来,我们将创建一个Core模块:

ng g m Core

In core/core.module.ts,我们将添加应用程序所需的模块:

// ...
从“@angular/fire”中导入{AngularFireModule}
从“@angular/fire/firestore”中导入{AngularFirestoreModule}
从“src/环境/环境”中导入{environment};
从“@angular/forms”中导入{ReactiveFormsModule}

@NgModule({
    // ...
    imports: [
        // ...
        AngularFireModule.initializeApp(环境.firebase),
        AngularFirestoreModule,
        ReactiveFormsModule,
    ],
    exports: [
        CommonModule,
        AngularFireModule,
        AngularFirestoreModule,
        ReactiveFormsModule
    ]
})
导出类CoreModule {}

现在,让我们从employees模块开始创建employees页面:

ng g m员工——路由

In employees-routing.module.ts, let’s add the employees route:

// ...
导入{EmployeesPageComponent}./组件/员工页/员工页.component';

// ...
const routes: routes = [
    {路径:'employees',组件:EmployeesPageComponent}
];
// ...

And in employees.module.ts, we’ll import ReactiveFormsModule:

// ...
从“@angular/forms”中导入{ReactiveFormsModule};
// ...

@NgModule({
    // ...
    imports: [
        // ...   
        ReactiveFormsModule
    ]
})
导出类EmployeesModule {}

现在,让我们将这两个模块添加到 app.module.ts file:

// ...
导入{EmployeesModule}./员工/员工.module';
导入{CoreModule}./core/core.module';

imports: [
    // ...
    CoreModule,
    EmployeesModule
],

Finally, 让我们创建员工页面的实际组件, 加上相应的模型, service, store, and state.

ngc employees/components/EmployeesPage
ng g c employees/components/EmployeesList
g c employees/components/EmployeesForm

对于我们的模型,我们需要一个名为 models/employee.ts:

导出接口Employee {
    id: string;
    name: string;
    location: string;
    hasDriverLicense:布尔;
}

我们的服务将驻留在一个名为 员工/服务/员工.firestore.ts. 此服务将扩展泛型 FirestoreService 之前创建的,我们会设置 basePath Firestore收藏的:

从“@angular/core”中导入{Injectable};
从“src/应用程序/核心/服务/ firestore”中导入{FirestoreService}.service';
导入{Employee}../模型/员工”;

@Injectable({
    providedIn:“根”
})
export class EmployeeFirestore extends FirestoreService {

    protected basePath: string = 'employees';

}

然后我们将创建文件 员工/州/员工页.ts. 这将作为雇员页面的状态:

导入{Employee}../模型/员工”;
导出接口EmployeesPage {

    loading: boolean;
    员工:员工[];
    formStatus:字符串;

}

国家会有 loading 值,该值确定是否在页面上显示加载消息,则 employees themselves, and a formStatus 变量来处理表单的状态(例如.g. Saving or Saved.)

我们需要一份文件 员工/服务/员工页.store.ts. 这里我们将扩展 StoreService created before. 我们将设置存储名称,这将在调试时用于识别它.

该服务将初始化并保存雇员页面的状态. 注意构造函数调用 super() 使用页面的初始状态. 在本例中,我们将初始化状态 loading=true 还有一排空的员工.

导入{EmployeesPage}../州/员工页”;
从src/应用程序/核心/服务/商店中导入{StoreService}.service';
从“@angular/core”中导入{Injectable};

@Injectable({
    providedIn:“根”
})
export class EmployeesPageStore extends StoreService {
    受保护的存储:string = 'employees-page';

    constructor() {
        super({
            loading: true,
            employees: [],
        })
    }
}

Now let’s create EmployeesService to integrate EmployeeFirestore and EmployeesPageStore:

g s employees/services/ employees

注意,我们正在注入 EmployeeFirestore and EmployeesPageStore in this service. 这意味着 EmployeesService 将包含并协调对Firestore和store的调用以更新状态. 这将帮助我们创建一个供组件调用的API.

导入{EmployeesPageStore}./employees-page.store';
导入{EmployeeFirestore}./employee.firestore';
从“@angular/core”中导入{Injectable};
从'rxjs'中导入{Observable};
导入{Employee}../模型/员工”;
从'rxjs/operators'中导入{tap, map};

@Injectable({
    providedIn:“根”
})
导出类EmployeesService {

    constructor(
        私有firestore: EmployeeFirestore;
        私有存储:EmployeesPageStore
    ) {
        this.firestore.collection$().pipe(
            tap(employees => {
                this.store.patch({
                    loading: false,
                    employees,        
                }, ' employees collection subscription ')
            })
        ).subscribe()
    }

    get employees$(): Observable {
        return this.store.state$.pipe(map(state => state.loading
            ? []
            : state.employees))
    }

    get loading$(): Observable {
        return this.store.state$.pipe(map(state => state.loading))
    }

    get noResults$(): Observable {
        return this.store.state$.pipe(
            map(state => {
                return !state.loading
                    && state.employees
                    && state.employees.length === 0
            })
        )
    }

    get formStatus$(): Observable {
        return this.store.state$.pipe(map(state => state.formStatus))
    }

    创建(employee: employee) {
        this.store.patch({
            loading: true,
            employees: [],
            formStatus:“拯救...'
        },“雇员创建”)
        return this.firestore.create(employee).then(_ => {
            this.store.patch({
                formStatus:“救了!'
            }, "employee create SUCCESS")
            setTimeout(() => this.store.patch({
                formStatus: ''
            },“员工创建超时重置表单状态”),2000)
        }).catch(err => {
            this.store.patch({
                loading: false,
                表单状态:'发生错误'
            }, "employee create ERROR")
        })
    }

    Delete (id: string): any {
        this.store.补丁({loading: true, employees: []}, "employee delete")
        return this.firestore.delete(id).catch(err => {
            this.store.patch({
                loading: false,
                表单状态:'发生错误'
            }, "employee delete ERROR")
        })
    }
}

让我们来看看该服务是如何工作的.

在构造函数中,我们将订阅Firestore雇员集合. 只要Firestore从集合中发出数据,我们就会更新存储设置 loading=false and employees 与Firestore的返回集合. 因为我们已经注射了 EmployeeFirestore,从Firestore返回的对象类型为 Employee,它支持更多的智能感知功能.

当应用程序处于活动状态时,此订阅将是有效的, 监听所有更改,并在每次Firestore流数据时更新存储.

this.firestore.collection$().pipe(
    tap(employees => {
        this.store.patch({
        loading: false,
        employees,        
        }, ' employees collection subscription ')
    })
).subscribe()

The employees$() and loading$() 函数将选择我们希望稍后在组件上使用的状态片段. employees$() 将返回一个空数组时,状态正在加载. 这将允许我们在视图上显示适当的消息传递.

get employees$(): Observable {
    return this.store.state$.pipe(map(state => state.loading ? [] : state.employees))
}

get loading$(): Observable {
    return this.store.state$.pipe(map(state => state.loading))
}

现在我们已经准备好了所有的服务,我们可以构建视图组件了. 但在此之前,快速复习一下可能会派上用场……

RxJs的observable和 async Pipe

可观察对象允许订阅者以流的形式接收数据发射. 这个,结合 async 管,可以很厉害.

The async pipe负责订阅Observable,并在发出新数据时更新视图. More importantly, 当组件被销毁时,它会自动取消订阅, 保护我们免受内存泄漏.

你可以阅读更多关于observable和RxJs库的信息 the official docs.

创建视图组件

In 员工/组件/员工页/员工页.component.html,我们将输入以下代码:

Employees

Likewise, 员工/组件/员工列表/员工列表.component.html 会有这个,用 async 上述管道技术:

Loading...
No results
{{employee.location}}
{{employee.name}}

{{employee.hasDriverLicense ? 'Can drive': ''}}

但在这种情况下,我们还需要为组件编写一些TypeScript代码. The file 员工/组件/员工列表/员工列表.component.ts will need this:

导入{Employee}../../模型/员工”;
从“@angular/core”中导入{Component, OnInit};
从'rxjs'中导入{Observable};
导入{EmployeesService}../../服务/员工.service';

@Component({
    选择器:“app-employees-list”,
    templateUrl: './employees-list.component.html',
    styleUrls: ['./employees-list.component.scss']
})
导出类EmployeesListComponent实现OnInit {
    loading$: Observable;
    employees$: Observable;
    noResults$: Observable;

    constructor(
        私人雇员: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);
    }

}

那么,进入浏览器,我们现在看到的是:

一个空的员工列表和消息“employees-form works”!"

控制台将有以下输出:

显示前后值变化的补丁事件.

看这个,我们可以看出Firestore流式传输了 employees 集合的空值,以及 employees-page 商店被打了补丁,设置 loading from true to false.

好了,让我们构建表单来向Firestore添加新员工:

雇员表格

In 员工/组件/ employees-form / employees-form.component.html 我们将添加以下代码:

请输入名称.
请选择位置.
{{ status$ | async }}

对应的TypeScript代码将驻留在 员工/组件/ employees-form / employees-form.component.ts:

导入{EmployeesService}./../../服务/员工.service';
从“@angular/fire/firestore”中导入{AngularFirestore};
从“@angular/core”中导入{Component, OnInit};
从“@angular/forms”中导入{FormGroup, FormControl, Validators};
从'rxjs'中导入{Observable};

@Component({
    选择器:“app-employees-form”,
    templateUrl: './employees-form.component.html',
    styleUrls: ['./employees-form.component.scss']
})
导出类EmployeesFormComponent实现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(
        私人雇员: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()
    }

}

表单将调用 create() method of EmployeesService. 现在页面看起来是这样的:

与之前相同的空员工列表,这次使用用于添加新员工的表单.

让我们看看添加新员工时会发生什么.

添加新员工

添加新员工后,我们将看到控制台的以下get输出:

补丁事件与Firestore事件混在一起, 编号1到6(本地创作), Firestore集合流, 本地收藏订阅, Firestore创造, 本地创作成功, 和本地创建超时形式状态重置.)

这些是添加新员工时触发的所有事件. 让我们仔细看看.

When we call create() 我们将执行以下代码,设置 loading=true, formStatus = '拯救...' and the employees array to empty ((1) 在上图中).

this.store.patch({
    loading: true,
    employees: [],
    formStatus:“拯救...'
},“雇员创建”)
return this.firestore.create(employee).then(_ => {
    this.store.patch({
        formStatus:“救了!'
    }, "employee create SUCCESS")
    setTimeout(() => this.store.patch({
        formStatus: ''
    },“员工创建超时重置表单状态”),2000)
}).catch(err => {
    this.store.patch({
        loading: false,
        表单状态:'发生错误'
    }, "employee create ERROR")
})

接下来,我们将调用基本的Firestore服务来创建记录日志的雇员 (4). 在promise回调中,我们设置 formStatus='Saved!' and log (5). 最后,我们将超时设置为 formStatus 回到空,日志 (6).

Log events (2) and (3) 事件是由Firestore订阅员工集合触发的吗. When the EmployeesService is instantiated, 我们订阅集合,并在每次发生变化时接收集合.

这将为带有的存储设置一个新状态 loading=false by setting the employees 数组表示来自Firestore的员工.

如果我们展开日志组, 我们将看到每个事件的详细数据和商店的更新, 使用前一个值和下一个值, 哪一个对调试有用.

先前的日志输出与所有状态管理详细信息展开.

这是添加新员工后页面的样子:

员工名单中有一张员工卡,表格在添加后还在填写.

添加摘要组件

假设我们现在想要在页面上显示一些汇总数据. 假设我们想要员工总数,有多少是司机,有多少来自罗萨里奥.

我们首先将新的状态属性添加到页面状态模型中 员工/州/员工页.ts:

// ...
导出接口EmployeesPage {

    loading: boolean;
    员工:员工[];
    formStatus:字符串;

    totalEmployees:数量;
    totalDrivers:数量;
    totalRosarioEmployees:数量;

}

我们会在store中初始化它们 员工/服务/ emplyees-page.store.ts:

// ...
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:员工.length,
            totalDrivers:员工.filter(employee => employee.hasDriverLicense).length,
            totalRosarioEmployees:员工.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))
}

// ...

现在,让我们创建summary组件:

ngc employees/components/EmployeesSummary

We’ll put this in 员工/组件/ employees-summary / employees-summary.html:

Total: {{total$ | async}}
Drivers: {{drivers$ | async}}
Rosario: {{rosario$ | async}}

And in 员工/组件/ employees-summary / employees-summary.ts:

从“@angular/core”中导入{Component, OnInit};
导入{EmployeesService}../../服务/员工.service';
从'rxjs'中导入{Observable};

@Component({
    选择器:“app-employees-summary”,
    templateUrl: './ employees-summary.component.html',
    styleUrls: ['./ employees-summary.component.scss']
})
导出类EmployeesSummaryComponent实现OnInit {

    total$: Observable < number > ;
    drivers$: Observable < number > ;
    rosario$: Observable < number > ;

    constructor(
        私人雇员:EmployeesService
    ) {}

    ngOnInit() {
        this.total$ = this.employees.totalEmployees$;
        this.drivers$ = this.employees.totalDrivers$;
        this.rosario$ = this.employees.totalRosarioEmployees美元;
    }

}

然后将组件添加到 员工/员工页/员工页.component.html:

// ...

Employees

// ...

结果如下:

Employees page, 现在总结一下上面的列表, 显示员工总数, 那些司机, 还有来自罗萨里奥的人.

在控制台我们有:

控制台输出显示补丁事件更改汇总值.

employees服务计算总数 totalEmployees, totalDrivers, and totalRosarioEmployees 并更新状态.

The 本教程的完整代码可在GitHub上获得,还有 a live demo.

使用可观察对象管理Angular应用状态!

In this tutorial, 我们介绍了一种在Angular应用中使用Firebase后端管理状态的简单方法.

这种方法非常符合Angular使用可观察对象的准则. 它还通过提供对应用程序状态的所有更新的跟踪来方便调试.

通用存储服务还可以用于管理不使用Firebase功能的应用程序的状态, 要么只管理应用程序的数据,要么管理来自其他api的数据.

但在你不加区分地应用这个之前,有一件事要考虑的是 EmployeesService 在构造函数上订阅Firestore,并在应用处于活动状态时保持侦听. 如果我们在应用程序的多个页面上使用员工列表,这可能会很有用, 以避免在页面之间导航时从Firestore获取数据.

但在其他情况下,这可能不是最好的选择,例如,如果您只需要一次拉出初始值,然后手动触发从Firebase重新加载数据. 底线是, 为了选择更好的实现方法,理解应用的需求总是很重要的.

关于总博客的进一步阅读:

了解基本知识

  • What is Angular?

    Angular(最初是AngularJS)是一个流行的用于创建单页面应用程序(spa)的前端框架。. 它是开源的,由b谷歌支持.

  • 什么是状态管理?

    状态管理是关于正确跟踪web应用程序中的变量. E.g. 如果聊天应用的用户切换了聊天室,这就是状态的变化. 如果他们发出了一个信息, 但是发送功能不知道早期的状态变化, 它将把消息发送到前一个聊天室, 导致非常糟糕的用户体验.

  • What is Firebase?

    b谷歌的Firebase是一个一体化的移动应用开发平台. 它以提供原始的实时数据库而闻名, 但现在包括综合事故报告, authentication, and asset hosting, among others.

就这一主题咨询作者或专家.
Schedule a call
华金·希德的头像
Joaquin Cid

Located in 阿根廷圣达菲省罗萨里奥

Member since May 2, 2018

About the author

Joaquin是一名全栈和混合移动应用程序开发人员,在WebMD和Getty Images等公司工作了超过12年的经验.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Previously At

Velocity Partners

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.