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
      }
    }
  );
});