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

