Рендеринг на серверной стороне в Angular Universal

Одностраничные приложения (Single-page Application, или SPA) состоят из одного HTML-документа, который передается клиенту. Любые новые представления, необходимые приложению, генерируются с помощью JavaScript исключительно на клиенте. Цикл запрос-ответ все еще необходим, но обычно он выполняется только для данных RESTful API или для сбора статических ресурсов, таких как изображения.

Такой способ написания приложений имеет много преимуществ. Однако кое-что все же теряется: например, поисковые роботы не могут просматривать ваше приложение, снижается производительность во время загрузки (и она может занять значительное время). Рендеринг на стороне сервера (SSR) поможет сгладить некоторые недостатки.

В этом мануале вы узнаете, как использовать Angular Universal для рендеринга на стороне сервера.

Требования

Это руководство было протестировано на Node v16.4.2, npm v7.19.1, @angular/core v12.1.1 и @nguniversal/express-engine v12.1.0.

1: Начало работы с Angular Universal

Приложение Angular — это одностраничное приложение, которое запускается только в браузере клиента. Однако Angular Universal позволяет также запускать его на сервере. Благодаря этому вы можете обслуживать статический HTML для клиента. Через Angular Universal сервер будет предварительно отображать страницы и показывать пользователям какой-то контент, пока клиентское приложение загружается в фоновом режиме. Как только на стороне клиента все будет готово, он плавно переключится с серверной стороны на клиентскую. Ваши пользователи не заметят никакой разницы: разве что вместо того, чтобы ждать, пока завершится загрузка, они, по крайней мере, смогут получить какой-то контент, который будет поддерживать их активность; после завершения загрузки они смогут начать использовать полнофункциональное клиентское приложение.

Для поддержки одностраничного приложения через Angular Universal нужно внести изменения как в клиентское приложение, так и в стек сервера. В этой статье мы используем свежее приложение Angular, созданное с помощью Angular CLI версии 7 и выше.

Читайте также: Angular 7: обновление и новые функции

Для запуска приложений Universal подходит почти любая серверная технология, но она должна иметь возможность вызывать специальную функцию renderModuleFactory(), предоставляемую Angular Universal (сама по себе эта функция является пакетом Node); поэтому в данном примере мы будем обслуживать это приложение Angular с сервера Node/Express.

Используем простую схему, чтобы быстро начать работу.

В каталоге вашего приложения откройте терминал и выполните следующую команду:

ng add @nguniversal/express-engine

Вы увидите, что схема внесла пару изменений в ваше приложение — отредактировала и создала несколько файлов. Рассмотрим эти изменения подробнее.

В файле angular.json строка projects.{{project-name}}.architect.build.options.outputPath заменена на строку “dist/browser”, а также сюда добавилась новая строка projects.{{project-name}}.architect по имени “server”. Это позволяет Angular CLI узнать, какие версии сервера и Universal использует приложение.

Также был обновлен файл package.json. Помимо новых зависимостей (чего и следовало ожидать), сюда внесены несколько новых скриптов:

  • “dev:ssr”
  • “build:ssr”
  • “serve:ssr”
  • “prerender”

После изменения файла main.ts версия приложения для браузера не начнет загрузку до тех пор, пока страницы, отрендеренные Universal, не будут полностью загружены.

Файл app.module.ts также был изменен, чтобы выполнять статический метод .withServerTransition в импортированном BrowserModule. Он сообщает браузерной версии приложения, что в какой-то момент клиент будет переходить с серверной версии.

Также команда создала новый файл server.ts. Это сервер NodeJS Express. Конечно, вам не обязательно использовать именно эту настройку сервера и именно в том виде, в каком она была сгенерирована, однако обратите внимание на строку:

server.engine('html', ngExpressEngine({ ... }))

ngExpressEngine — это оболочка над renderModuleFactory, благодаря которой работает Universal. Если вы не используете такой файл server.ts в своей настройке, по крайней мере скопируйте эту строку и интегрируйте ее в свой файл.

Еще один новый файл – tsconfig.server.json. Он сообщает компилятору Angular, где найти входной модуль для приложения Universal.

Следующий новый файл называется app.server.module.ts. Это корневой модуль, предназначенный только для серверной версии. Обратите внимание, что он импортирует AppModule, а также ServerModule из @angular/platform-server и загружает тот же AppComponent, что и AppModule. AppServerModule — это точка входа приложения Universal.

Кроме того, команда создала файл main.server.ts. Он в основном экспортирует только AppServerModule, который является точкой входа приложения Universal. Скоро мы вернемся к этому файлу.

2: Запуск приложения Universal

Из командной строки выполните следующую команду:

npm run build:ssr

А после этого запустите:

npm run serve:ssr

Если вы не столкнулись с трудностями в процессе сборки, откройте в браузере адрес http://localhost:4000 (или тот порт, который вы используете). Вы увидите приложение Universal в действии. Ваше приложение визуально никак не изменится, но первая страница будет загружаться намного быстрее, чем обычно. Если ваше приложение маленькое и простое, это трудно заметить.

Вы можете попробовать снизить скорость сети, открыв Chrome Dev Tools и найдя выпадающий список Online во вкладке Network. Выберите Slow 3G, чтобы имитировать работу в медленной сети — приложение по-прежнему должно показывать хорошую производительность как для целевой страницы, так и для тех страниц, на которые вы переходите.

Кроме того, попробуйте просмотреть исходный код страницы (кликните правой кнопкой мыши на страницу и выберите View Page Source). Вы увидите обычный HTML-код в теге <body>, который соответствует тому, что отображается на вашей странице. Это означает, что поисковой робот может легко проанализировать ваше приложение. Сравните это с исходным кодом страницы приложения без Universal: все, что вы увидите в теге <body>, это <app-root> (или что-то другое, что вы используете в качестве селектора для AppComponent).

Заключение

В этом мануале мы поговорили о том, как использовать Angular Universal для рендеринга на стороне сервера.

Поскольку приложение Universal запускается на сервере, а не в браузере, в его коде необходимо учитывать несколько моментов:

  • Проверьте, используете ли вы специфичные для браузера объекты, такие как window, document или location. На сервере они не нужны, и вы не должны использовать их в любом случае; попробуйте использовать инъекцию Angular, например Document или Location. В крайнем случае, если эти объекты вам действительно нужны, оберните их в условный оператор, чтобы они использовались Angular только в браузере. Для этого можно импортировать функции isPlatformBrowser и isPlatformServer из @angular/common. После этого можно внедрить токен PLATFORM_ID в компонент и запустить импортированные функции, чтобы увидеть, где вы находитесь – на сервере или в браузере.
  • Если вы используете ElementRef для получения дескриптора HTML-элемента, не используйте nativeElement для управления атрибутами элемента. Вместо этого внедрите Renderer2 и используйте один из его доступных методов.
  • Обработка событий браузера не будет работать. Ваше приложение не будет реагировать на клики или другие события браузера во время серверной обработки. Однако навигация по ссылкам, сгенерированным из routerLink, будет работать корректно.
  • По возможности избегайте использования setTimeout.
  • Сделайте все URL-адреса для запросов к серверу абсолютными. Запросы данных по относительным URL-адресам на серверной стороне не будут выполняться, даже если сервер может обрабатывать такие URL-адреса.
  • Также следует помнить, что безопасность HTTP-запросов, отправленных с сервера, отличается от запросов, отправленных из браузера. Запросы сервера могут иметь другие требования к безопасности и функции. Обеспечивать безопасность этих запросов вам придется самостоятельно.

Чтобы узнать больше об Angular Universal, обратитесь к официальной документации.

Tags: , ,

Добавить комментарий