【问题标题】:Angular universal: Route resolver + Firebase loading timeoutAngular Universal:路由解析器 + Firebase 加载超时
【发布时间】:2021-09-22 11:52:31
【问题描述】:

我遇到了一个问题,需要帮助。 我有一个组件等待一些 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 &amp;&amp; npm run serve:ssr

我不确定自己做错了什么,因为我没有收到任何错误,但只有云功能超时消息。

【问题讨论】:

    标签: angular firebase google-cloud-firestore google-cloud-functions server-side-rendering


    【解决方案1】:

    我不知道确切的答案,尽管我知道云功能不是为 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);
      }
    );
    

    【讨论】:

    • 您好 Poul,感谢您提供如此详细的回答。我进行了建议的更改,但数据在组件中以undefined 的形式返回。我尝试将解析器代码包装在 Angular 区域之外运行,这可以工作,但它在 firebase 上的已部署版本中停止工作,它会无限加载
    • 当使用ng deploy --preview SSR 有效但部署时无效
    • @teebo SSR 是否有权访问 Firestore 数据?您可以尝试在管道中添加catchError(() =&gt; of([])) 吗?
    • 我找到了一个临时解决方案,我无法进行另一次部署,因为当前的解决方案有效,因为我注意到您共享的解决方案有助于并与 ng deploy --preview 一起使用,但在部署到 firebase 时不起作用所以我将无法在本地检查 observable 的错误
    • 另外,我可以建议你看看 scully 的 SSR + angular
    【解决方案2】:

    因为目标是为每篇文章动态设置元标记;因为解析器要带我兜风; 我的解决方案如下,没有解析器:

    我已将状态传输服务注入到我的组件构造函数中;这在这个解决方案中起了很大的作用

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

    【讨论】:

      猜你喜欢
      • 2019-09-30
      • 2020-08-04
      • 1970-01-01
      • 1970-01-01
      • 2021-11-23
      • 1970-01-01
      • 1970-01-01
      • 2019-04-12
      • 1970-01-01
      相关资源
      最近更新 更多