Angular Universal is an open source project that extends @ angular / platform-server functionality. It makes Server Side Rendering possible in Angular.
Angular Universal supports multiple backends:
Socket Engine β -, SSR- . , Angular Universal Express, .
Angular Universal
Angular DOM node.js β domino. GET- domino , .
Angular . , DOM, . DOM . HTML GET-. Angular- .
SSR Angular
1.
. , . , .
, Angular SSR. , , Zone.js ApplicationRef.
Zone.js β , . Angular . Angular .
ApplicationRef β (docs). ApplicationRef#isStable. Observable, boolean. isStable true , Angular , false β .
, β , Angular
, Angular . .
, , . setInterval, rxjs.interval , Angular, . HTTP- . .
, timeout:
import { timeout, catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';
http.get('https://example.com')
.pipe(
timeout(2000),
catchError(e => of(null))
).subscribe()
, .
:
;
timeout .
NgxSsrTimeoutModule @ngx-ssr/timeout. . AppServerModule, HTTP- .
import { NgModule } from '@angular/core';
import {
ServerModule,
} from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { NgxSsrTimeoutModule } from '@ngx-ssr/timeout';
@NgModule({
imports: [
AppModule,
ServerModule,
NgxSsrTimeoutModule.forRoot({ timeout: 500 }),
],
bootstrap: [AppComponent],
})
export class AppServerModule {}
Angular NgZone.
import { Injectable, NgZone } from "@angular/core";
@Injectable()
export class SomeService {
constructor(private ngZone: NgZone){
this.ngZone.runOutsideAngular(() => {
interval(1).subscribe(() => {
// somo code
})
});
}
}
tuiZonefree @taiga-ui/cdk:
import { Injectable, NgZone } from "@angular/core";
import { tuiZonefree } from "@taiga-ui/cdk";
@Injectable()
export class SomeService {
constructor(private ngZone: NgZone){
interval(1).pipe(tuiZonefree(ngZone)).subscribe()
}
}
. , (. β 7). , .
2. Β« Β»
. , 2 . . 2 , .
, , , , , html . html, .
. : in-memory cache .
. . :
Cache-Control: max-age=31536000
.
In-memory cache. In-memory cache , API- . @ngx-ssr/cache
.
API- , NgxSsrCacheModule AppModule.
maxSize . 50 , 50 GET-, .
maxAge , .
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { NgxSsrCacheModule } from '@ngx-ssr/cache';
import { environment } from '../environments/environment';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
NgxSsrCacheModule.configLruCache({ maxAge: 10 * 60_000, maxSize: 50 }),
],
bootstrap: [AppComponent],
})
export class AppModule {}
html.
, @ngx-ssr/cache @ngx-ssr/cache/express. withCache. .
import { ngExpressEngine } from '@nguniversal/express-engine';
import { LRUCache } from '@ngx-ssr/cache';
import { withCache } from '@ngx-ssr/cache/express';
server.engine(
'html',
withCache(
new LRUCache({ maxAge: 10 * 60_000, maxSize: 100 }),
ngExpressEngine({
bootstrap: AppServerModule,
})
)
);
3. ReferenceError: localStorage is not defined
localStorage . . : ReferenceError: localStorage is not defined.
Angular- API. , node.js document. DOCUMENT DI.
API . DI. DI .
:
import {Component, Inject, NgModule} from '@angular/core';
import {LOCAL_STORAGE} from '@ng-web-apis/common';
@Component({...})
export class SomeComponent {
constructor(@Inject(LOCAL_STORAGE) localStorage: Storage) {
localStorage.getItem('key');
}
}
LOCAL_STORAGE @ng-web-apis/common. . UNIVERSAL_LOCAL_STORAGE @ng-web-apis/universal AppServerModule β LOCAL_STORAGE localStorage .
import { NgModule } from '@angular/core';
import {
ServerModule,
} from '@angular/platform-server';
import { AppModule } from './app.module';
import { AppComponent } from './app.component';
import { UNIVERSAL_LOCAL_STORAGE } from '@ngx-ssr/timeout';
@NgModule({
imports: [
AppModule,
ServerModule,
],
providers: [UNIVERSAL_LOCAL_STORAGE],
bootstrap: [AppComponent],
})
export class AppServerModule {}
4.
, :
@Component({
selector: 'ram-root',
template: '<some-omp *ngIf="isServer"></some-omp>',
styleUrls: ['./app.component.less'],
})
export class AppComponent {
isServer = isPlatformServer(this.platformId);
constructor(@Inject(PLATFORM_ID) private platformId: Object){}
}
PLATFORM_ID, . ngIf.
C DI .
.
export const IS_SERVER_PLATFORM = new InjectionToken<boolean>('Is server?', {
factory() {
return isPlatformServer(inject(PLATFORM_ID));
},
});
IS_SERVER_PLATFORM : .
@Directive({
selector: '[ifIsServer]',
})
export class IfIsServerDirective {
constructor(
@Inject(IS_SERVER_PLATFORM) isServer: boolean,
templateRef: TemplateRef<any>,
viewContainer: ViewContainerRef
) {
if (isServer) {
viewContainer.createEmbeddedView(templateRef);
}
}
}
IfIsBowser.
.
@Component({
selector: 'ram-root',
template: '<some-omp *ifIsServer"></some-omp>',
styleUrls: ['./app.component.less'],
})
export class AppComponent {}
. . . , @ngx-ssr/platform.
5. Memory leak
interval .
import { Injectable, NgZone } from "@angular/core";
import { interval } from "rxjs";
@Injectable()
export class LocationService {
constructor(ngZone: NgZone) {
ngZone.runOutsideAngular(() => interval(1000).subscribe(() => {
...
}));
}
}
, , subscribe, . . .
ngOnDestoroy. , . . , :
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();
}
}
6.
. . index.html.
. . β 1. , ? ?
.
7.
, , , .
Angular , . html .
. , . roadmap Angular Universal Β«Full client rehydration strategy that reuses DOM elements/CSS rendered on the serverΒ».
Angular Universal β . .
, - Angular Universal , . , , - .