写点什么

Angular 服务器端渲染应用一个常见的内存泄漏问题

作者:Jerry Wang
  • 2022 年 6 月 23 日
  • 本文字数:1609 字

    阅读完需:约 5 分钟

Angular 服务器端渲染应用一个常见的内存泄漏问题

考虑如下的 Angular 代码:


import { Injectable, NgZone } from "@angular/core";import { interval } from "rxjs";
@Injectable()export class LocationService { constructor(ngZone: NgZone) { ngZone.runOutsideAngular(() => interval(1000).subscribe(() => { ... })); }}
复制代码


这段代码不会影响应用程序的稳定性,但是如果应用程序在服务器上被销毁,传递给订阅的回调将继续被调用。 服务器上应用程序的每次启动都会以 interval 的形式留下一个 artifact.


这是一个潜在的内存泄漏点。


这个内存泄漏风险可以通过使用 ngOnDestoroy 钩子解决。这个钩子适用于 Component 和 service. 我们需要保存 interval 返回的订阅(subscription),并在服务被销毁时终止它。


退订 subscription 的技巧有很多,下面是一个例子:


import { Injectable, NgZone, OnDestroy } from "@angular/core";import { interval, Subscription } from "rxjs";
@Injectable()export class LocationService implements OnDestroy { private subscription: Subscription;
constructor(ngZone: NgZone) { this.subscription = ngZone.runOutsideAngular(() => interval(1000).subscribe(() => {}) ); }
ngOnDestroy(): void { this.subscription.unsubscribe(); }}
复制代码

屏幕闪烁问题

用户的浏览器显示从服务器渲染并返回的页面,一瞬间出现白屏,闪烁片刻,然后应用程序开始运行,看起来一切正常。出现闪烁的原因,在于 Angular 不知道如何重用它在服务器上成功渲染的内容。在客户端环境中,它从根元素中 strip 所有 HTML 并重新开始绘制。


闪烁问题可以抽象成如下步骤:


关于正在发生的事情的一个非常简化的解释:


(1) 用户访问应用程序(或刷新)


(2) 服务器在服务器中构建 html


(3) 它被发送到用户的浏览器端


(4) Angular 重新创建 应用程序(就好像它是一个常规的非 Angular Universal 程序)


(5) 当上述四个步骤发生时,用户会看到一个 blink 即闪烁的屏幕。


代码如下:


// You can see this by adding:// You should see a console log in the server// `Running on the server with appId=my-app-id`// and then you'll see in the browser console something like// `Running on the browser with appId=my-app-id`export class AppModule {  constructor(    @Inject(PLATFORM_ID) private platformId: Object,    @Inject(APP_ID) private appId: string) {    const platform = isPlatformBrowser(this.platformId) ?      'in the browser' : 'on the server';    console.log(`Running ${platform} with appId=${this.appId}`);  }}
复制代码

无法通过 API 的方式终止渲染

什么时候需要人为干预的方式终止一个服务器端渲染?


始终明确一点,渲染应用程序的时间点发生在应用程序 applicationRef.isStable 返回 true 时,参考下列代码:


https://github.com/angular/angular/blob/11.1.x/packages/platform-server/src/utils.ts#L43


function _render<T>(    platform: PlatformRef, moduleRefPromise: Promise<NgModuleRef<T>>): Promise<string> {  return moduleRefPromise.then((moduleRef) => {    const transitionId = moduleRef.injector.get(ɵTRANSITION_ID, null);    if (!transitionId) {      throw new Error(          `renderModule[Factory]() requires the use of BrowserModule.withServerTransition() to ensurethe server-rendered app can be properly bootstrapped into a client app.`);    }    const applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);    return applicationRef.isStable.pipe((first((isStable: boolean) => isStable)))        .toPromise()        .then(() => {          const platformState = platform.injector.get(PlatformState);         ...
复制代码


发布于: 刚刚阅读数: 3
用户头像

Jerry Wang

关注

🏆InfoQ写作平台-签约作者🏆 2017.12.03 加入

SAP成都研究院开发专家,SAP社区导师,SAP中国技术大使。2007 年从电子科技大学计算机专业硕士毕业后加入 SAP 成都研究院工作至今。工作中使用 ABAP, Java, JavaScript 和 TypeScript 进行开发。

评论

发布
暂无评论
Angular 服务器端渲染应用一个常见的内存泄漏问题_typescript_Jerry Wang_InfoQ写作社区