Angular?服務器端渲染應用常見的內(nèi)存泄漏問題小結
考慮如下的 Angular 代碼:
import { Injectable, NgZone } from "@angular/core"; import { interval } from "rxjs"; @Injectable() export class LocationService { constructor(ngZone: NgZone) { ngZone.runOutsideAngular(() => interval(1000).subscribe(() => { ... })); } }
這段代碼不會影響應用程序的穩(wěn)定性,但是如果應用程序在服務器上被銷毀,傳遞給訂閱的回調(diào)將繼續(xù)被調(diào)用。 服務器上應用程序的每次啟動都會以 interval 的形式留下一個 artifact.
這是一個潛在的內(nèi)存泄漏點。
這個內(nèi)存泄漏風險可以通過使用 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(); } }
屏幕閃爍問題
用戶的瀏覽器顯示從服務器渲染并返回的頁面,一瞬間出現(xiàn)白屏,閃爍片刻,然后應用程序開始運行,看起來一切正常。出現(xiàn)閃爍的原因,在于 Angular 不知道如何重用它在服務器上成功渲染的內(nèi)容。在客戶端環(huán)境中,它從根元素中 strip 所有 HTML 并重新開始繪制。
閃爍問題可以抽象成如下步驟:
關于正在發(fā)生的事情的一個非常簡化的解釋:
(1) 用戶訪問應用程序(或刷新)
(2) 服務器在服務器中構建html
(3) 它被發(fā)送到用戶的瀏覽器端
(4) Angular 重新創(chuàng)建
應用程序(就好像它是一個常規(guī)的非 Angular Universal 程序)
(5) 當上述四個步驟發(fā)生時,用戶會看到一個 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 的方式終止渲染
什么時候需要人為干預的方式終止一個服務器端渲染?
始終明確一點,渲染應用程序的時間點發(fā)生在應用程序 applicationRef.isStable 返回 true 時,參考下列代碼:
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 ensure the 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); ...
到此這篇關于Angular 服務器端渲染應用一個常見的內(nèi)存泄漏問題的文章就介紹到這了,更多相關Angular內(nèi)存泄漏內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解如何使用webpack+es6開發(fā)angular1.x
本篇文章主要介紹了詳解如何使用webpack+es6開發(fā)angular1.x,具有一定的參考價值,有興趣的可以了解一下2017-08-08AngularJS基于ngInfiniteScroll實現(xiàn)下拉滾動加載的方法
這篇文章主要介紹了AngularJS基于ngInfiniteScroll實現(xiàn)下拉滾動加載的方法,結合實例形式分析AngularJS下拉滾動插件ngInfiniteScroll的下載、功能、屬性及相關使用方法,需要的朋友可以參考下2016-12-12AngularJS中控制器函數(shù)的定義與使用方法示例
這篇文章主要介紹了AngularJS中控制器函數(shù)的定義與使用方法,結合具體實例形式分析了AngularJS控制器函數(shù)的定義、綁定及相關使用技巧,需要的朋友可以參考下2017-10-10深入解析AngularJS框架中$scope的作用與生命周期
這篇文章主要介紹了AngularJS中$scope的作用與生命周期,包括在DOM中添加controller對象的相關用法,需要的朋友可以參考下2016-03-03