Как реализовать SSR с Angular Universal?angular-66

Angular Universal — это технология, которая позволяет выполнять рендеринг Angular-приложений на стороне сервера (Server-Side Rendering, SSR). Это улучшает SEO, время первой загрузки и производительность на слабых устройствах. Рассмотрим пошаговую реализацию.


1. Установка Angular Universal

Добавляем Universal в существующий проект:

ng add @nguniversal/express-engine

Эта команда:

  • Создает серверную часть на Express.
  • Добавляет необходимые файлы (server.ts, main.server.ts).
  • Обновляет конфигурацию Angular.

2. Структура проекта после установки

Ключевые файлы:

  • server.ts — Express-сервер для рендеринга.
  • app.module.server.ts — корневой модуль для серверного рендеринга.
  • main.server.ts — точка входа для серверного приложения.

3. Адаптация приложения для SSR

Проблемы и решения:

a) Окружение

Серверный рендеринг требует проверки платформы:

import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';

constructor(@Inject(PLATFORM_ID) private platformId: Object) {
  if (isPlatformBrowser(this.platformId)) {
    // Код выполнится только в браузере
    console.log('Running in browser');
  }
  if (isPlatformServer(this.platformId)) {
    // Код выполнится только на сервере
    console.log('Running on server');
  }
}

b) Работа с DOM

Используйте Renderer2 или проверку isPlatformBrowser:

if (isPlatformBrowser(this.platformId)) {
  window.scrollTo(0, 0);
}

c) HTTP-запросы

Используйте TransferState для предотвращения дублирования запросов:

import { TransferState, makeStateKey } from '@angular/platform-browser';

const DATA_KEY = makeStateKey<any>('data');

constructor(
  private http: HttpClient,
  private transferState: TransferState
) {}

getData() {
  const storedData = this.transferState.get(DATA_KEY, null);
  if (storedData) {
    return of(storedData);
  }
  return this.http.get('/api/data').pipe(
    tap(data => {
      if (isPlatformServer(this.platformId)) {
        this.transferState.set(DATA_KEY, data);
      }
    })
  );
}

4. Запуск и сборка

Для разработки:

npm run dev:ssr

Продакшен-сборка:

npm run build:ssr && npm run serve:ssr

5. Оптимизации

a) Pre-rendering

Для статических страниц можно использовать prerender:

npm run prerender

b) Cache Control

Настройка кэширования в server.ts:

server.get('*', (req, res) => {
  res.setHeader('Cache-Control', 'public, max-age=600');
  // ...
});

c) Critical CSS

Используйте critters для инлайнинга критического CSS:

// в angular.json
"configurations": {
  "production": {
    "optimization": {
      "styles": {
        "inlineCritical": true
      }
    }
  }
}

6. Развертывание

Популярные варианты:

  • Node.js сервер: Простое развертывание на Heroku, VPS.
  • Serverless: AWS Lambda, Firebase Functions.
  • Docker: Упаковка приложения в контейнер.

Пример Dockerfile:

FROM node:16-alpine
WORKDIR /app
COPY . .
RUN npm install && npm run build:ssr
EXPOSE 4000
CMD ["npm", "run", "serve:ssr"]

Резюмируем

  1. Установка: ng add @nguniversal/express-engine добавляет SSR.
  2. Адаптация: Проверка платформы, работа с DOM и HTTP-запросами.
  3. TransferState: Предотвращает дублирование запросов.
  4. Запуск: build:ssr и serve:ssr для продакшена.
  5. Оптимизации: Pre-rendering, кэширование, Critical CSS.
  6. Развертывание: Node.js, Serverless или Docker.

Angular Universal значительно улучшает производительность и SEO, но требует адаптации кода для работы в SSR-режиме. Для простых проектов можно начать с prerendering, для сложных — с полноценного SSR.