Angular 通用:路由解析器 + Firebase 加载超时
Angular universal: Route resolver + Firebase loading timeout
我遇到了一个问题,需要帮助。
我有一个组件等待一些 Firebase/Firestore 数据被路由器解析器解析,如下所示。
import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class ArticleDataResolver implements Resolve<any> {
constructor(
private angularFirestore: AngularFirestore,
) { }
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any> | Promise<any> | any {
const stateKey = state.url;
const ref = this.angularFirestore.collection("articles");
return ref
.doc(route.params.id)
.get()
.pipe(
map((dataSnap) => {
const articleData = dataSnap.data();
return articleData;
})
);
}
}
在消费组件中,我订阅了如下解析的可观察数据。
this.activatedRoute.data.subscribe(
(data) => {
console.log(data);
}
);
这段代码工作正常,但在刷新页面或直接导航到页面时停止工作,当使用如下云功能通过 SSR 提供服务时。
export const ssr = functions.runWith({
timeoutSeconds: 300,
memory: "1GB"
}).https.onRequest((request, response) => {
require(`${process.cwd()}/dist/motif/server`).app(request, response);
});
但即使在页面刷新和直接导航时,相同的代码也适用于我的 Node/Express 服务器。对于本地测试,我使用以下脚本:npm run build:ssr && npm run serve:ssr
我不确定我做错了什么,因为我没有收到任何错误,只有云功能超时消息。
我不知道确切的答案,尽管我知道云函数不是服务 SSR 应用程序的合适方式。尤其是在冷启动的情况下,您的页面响应可能会有相当大的延迟,从而违背了 SSR 的目的。
但是,据我所知,解析器返回的 Observable
需要完成。您可以找到有关此问题的完整讨论 here。这意味着您需要将 take(1)
添加到 pipe
:
@Injectable({ providedIn: 'root' })
export class ArticleDataResolver implements Resolve<any> {
constructor(
private angularFirestore: AngularFirestore,
) { }
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any> | Promise<any> | any {
const stateKey = state.url;
const ref = this.angularFirestore.collection("movementArticles");
return ref
.doc(route.params.id)
.get()
.pipe(
map((dataSnap) => {
const articleData = dataSnap.data();
return articleData;
}),
take(1)
);
}
}
然而,这违背了 firestore 的目的,因为当您不在路线上来回移动时,它不会更新。你可以这样做:
@Injectable({ providedIn: 'root' })
export class ArticleDataResolver implements Resolve<any> {
constructor(
private angularFirestore: AngularFirestore,
) { }
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const obs = this.angularFirestore.collection("movementArticles")
.doc(route.params.id)
.get()
.pipe(
map((snapshot) => snapshot.data(),
shareReplay(1)
);
return obs.pipe(
take(1),
mapTo(obs)
);
}
}
您实际设置 Observable
作为解析器的结果,但仅在它返回一次之后。我加了一个shareReplay(1)
。请注意,Firestore 系列不会因此而取消订阅。您可以添加一个 takeUntil
语句来检查路由更改以了解何时取消订阅。
您需要通过访问 .snapshot
来像这样在您的组件中使用,其中将包含您在 mapTo
:
中使用的可观察对象
this.activatedRoute.snapshot.data.subscribe(
(data) => {
console.log(data);
}
);
因为目标是为每篇文章动态设置元标记;并且因为解析器带我去兜风;
我的解决方案如下,没有解析器:
我已经将状态传输服务注入到我的组件构造函数中;在这个解决方案中发挥了重要作用
...// in the constructor
private state: TransferState
...
this.activatedRoute.params.subscribe((params) => {
this.selectedArticleID = params.id;
this.zone.runOutsideAngular(
() => {
let ARTICLE_KEY = makeStateKey(`${this.selectedArticleID}`)
this.selectedArticle = this.state.get(ARTICLE_KEY, null);
const ref = this.angularFirestore.collection("articles");
if (!this.selectedArticle) {
this.subscriptions.add(
ref
.doc(this.selectedArticleID)
.get()
.subscribe((dataSnap) => {
this.templateString = "";
// do somethings
// update meta tags
})
);
} else {
// update meta tags using the server data from the state
}
}
);
});
我遇到了一个问题,需要帮助。 我有一个组件等待一些 Firebase/Firestore 数据被路由器解析器解析,如下所示。
import { Injectable } from '@angular/core';
import { AngularFirestore } from '@angular/fire/firestore';
import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class ArticleDataResolver implements Resolve<any> {
constructor(
private angularFirestore: AngularFirestore,
) { }
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any> | Promise<any> | any {
const stateKey = state.url;
const ref = this.angularFirestore.collection("articles");
return ref
.doc(route.params.id)
.get()
.pipe(
map((dataSnap) => {
const articleData = dataSnap.data();
return articleData;
})
);
}
}
在消费组件中,我订阅了如下解析的可观察数据。
this.activatedRoute.data.subscribe(
(data) => {
console.log(data);
}
);
这段代码工作正常,但在刷新页面或直接导航到页面时停止工作,当使用如下云功能通过 SSR 提供服务时。
export const ssr = functions.runWith({
timeoutSeconds: 300,
memory: "1GB"
}).https.onRequest((request, response) => {
require(`${process.cwd()}/dist/motif/server`).app(request, response);
});
但即使在页面刷新和直接导航时,相同的代码也适用于我的 Node/Express 服务器。对于本地测试,我使用以下脚本:npm run build:ssr && npm run serve:ssr
我不确定我做错了什么,因为我没有收到任何错误,只有云功能超时消息。
我不知道确切的答案,尽管我知道云函数不是服务 SSR 应用程序的合适方式。尤其是在冷启动的情况下,您的页面响应可能会有相当大的延迟,从而违背了 SSR 的目的。
但是,据我所知,解析器返回的 Observable
需要完成。您可以找到有关此问题的完整讨论 here。这意味着您需要将 take(1)
添加到 pipe
:
@Injectable({ providedIn: 'root' })
export class ArticleDataResolver implements Resolve<any> {
constructor(
private angularFirestore: AngularFirestore,
) { }
resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<any> | Promise<any> | any {
const stateKey = state.url;
const ref = this.angularFirestore.collection("movementArticles");
return ref
.doc(route.params.id)
.get()
.pipe(
map((dataSnap) => {
const articleData = dataSnap.data();
return articleData;
}),
take(1)
);
}
}
然而,这违背了 firestore 的目的,因为当您不在路线上来回移动时,它不会更新。你可以这样做:
@Injectable({ providedIn: 'root' })
export class ArticleDataResolver implements Resolve<any> {
constructor(
private angularFirestore: AngularFirestore,
) { }
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const obs = this.angularFirestore.collection("movementArticles")
.doc(route.params.id)
.get()
.pipe(
map((snapshot) => snapshot.data(),
shareReplay(1)
);
return obs.pipe(
take(1),
mapTo(obs)
);
}
}
您实际设置 Observable
作为解析器的结果,但仅在它返回一次之后。我加了一个shareReplay(1)
。请注意,Firestore 系列不会因此而取消订阅。您可以添加一个 takeUntil
语句来检查路由更改以了解何时取消订阅。
您需要通过访问 .snapshot
来像这样在您的组件中使用,其中将包含您在 mapTo
:
this.activatedRoute.snapshot.data.subscribe(
(data) => {
console.log(data);
}
);
因为目标是为每篇文章动态设置元标记;并且因为解析器带我去兜风; 我的解决方案如下,没有解析器:
我已经将状态传输服务注入到我的组件构造函数中;在这个解决方案中发挥了重要作用
...// in the constructor
private state: TransferState
...
this.activatedRoute.params.subscribe((params) => {
this.selectedArticleID = params.id;
this.zone.runOutsideAngular(
() => {
let ARTICLE_KEY = makeStateKey(`${this.selectedArticleID}`)
this.selectedArticle = this.state.get(ARTICLE_KEY, null);
const ref = this.angularFirestore.collection("articles");
if (!this.selectedArticle) {
this.subscriptions.add(
ref
.doc(this.selectedArticleID)
.get()
.subscribe((dataSnap) => {
this.templateString = "";
// do somethings
// update meta tags
})
);
} else {
// update meta tags using the server data from the state
}
}
);
});