Angular Universal: Кеширование серверных запросов
Если вы создаете свой SPA с помощью Angular Universal, возможно, вы заметили, что при загрузке страницы содержимое «мигает» и после загружается снова. Это может происходить из-за того, что выполняются повторяющиеся запросы, сначала на сервере, а затем на клиенте. Что бы этого избежать, Angular Universal предоставил модуль TransferHttpCacheModule, который позволяет кешировать запросы, сделанные при рендеринге страницы на стороне сервера.
Если вы еще не работали с Angular Universal, как раз самое время попробовать — здесь можно найти полную инструкцию.
Настроить кеширование на стороне сервера довольно просто. Все, что вам нужно сделать, это добавить TransferHttpCacheModule в свой app.module.ts
и ServerTransferStateModule в свой app.server.module.ts.
// app.module.ts
import { TransferHttpCacheModule } from '@nguniversal/common';
@NgModule({
imports: [
(...)
TransferHttpCacheModule
]
})
export class AppModule {}
// app.server.module.ts
import { ServerTransferStateModule } from '@angular/platform-server';
@NgModule({
imports: [
(...)
ServerTransferStateModule
]
})
export class AppServerModule {}
После вы увидите, что запросы, выполненные на сервере во время рендеринга страницы, не будут дублироваться в браузере.
TransferHttpCacheModule предоставляет перехватчик HTTP, который использует TransferState из @angular/platform-browser
. TransferState — это хранилище значений ключей, которое передается из приложения на стороне сервера в приложение на стороне клиента, согласно документации Angular. При перехвате запроса он проверяет кеш (хранилище значений ключей), чтобы узнать, был ли сделан этот конкретный запрос и если ключ запроса найден в хранилище, он обработает запрос, вернув кешированный ответ.
import { TransferState } from '@angular/platform-browser';
...
if (this.transferState.hasKey(storeKey)) {
// Request found in cache. Respond using it.
const response = this.transferState.get(storeKey, {} as TransferHttpResponse);
return observableOf(new HttpResponse({
body: response.body,
headers: new HttpHeaders(response.headers),
status: response.status,
statusText: response.statusText,
url: response.url,
}));
}
Если ключ запроса не найден в кэше, он переходит к исходному запросу и устанавливает пару «ключ-значение» в кеш TransferState с ответом.
else {
// Request not found in cache. Make the request and cache it.
const httpEvent = next.handle(req);
return httpEvent
.pipe(
tap((event: HttpEvent) => {
if (event instanceof HttpResponse) {
this.transferState.set(storeKey, {
body: event.body,
headers: getHeadersMap(event.headers),
status: event.status,
statusText: event.statusText,
url: event.url || '',
});
}
})
);
}
Обработка запросов относительного пути
Вы можете столкнуться с проблемами при использовании относительных путей в запросах на стороне сервера. В этом случае вы можете использовать HTTP-перехватчик, чтобы преобразовать относительные пути в абсолютные.
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { Inject, Injectable } from '@angular/core';
import { REQUEST } from '@nguniversal/express-engine/tokens';
import { Request } from 'express';
function isAbsoluteUrl(url: string) {
return url.startsWith('http') || url.startsWith('//');
}
@Injectable()
export class ServerStateInterceptor implements HttpInterceptor {
constructor(@Inject(REQUEST) private request: Request) {}
intercept(
req: HttpRequest,
next: HttpHandler
): Observable<HttpEvent> {
if (this.request && !isAbsoluteUrl(req.url)) {
const protocolHost = `${this.request.protocol}://${this.request.get(
'host'
)}`;
const pathSeparator = !req.url.startsWith('/') ? '/' : '';
const url = protocolHost + pathSeparator + req.url;
const serverRequest = req.clone({ url });
return next.handle(serverRequest);
}
return next.handle(req);
}
}
Этот перехватчик явно работает с движком Express поэтому если вы используете другой движок, вам придется его адаптировать.
Источники: www.stijit.com, web-creator.ru