Рендеринг на стороне сервера (SSR): введение в Angular Universal
Обычное приложение Angular выполняется в браузере, отображая страницы в DOM в ответ на действия пользователя. Angular Universal запускается на сервере, генерируя статические страницы приложения, которые впоследствии загружаются на клиент. Это означает, что приложение обычно рендерится быстрее, давая пользователям возможность просматривать макет приложения до того, как оно станет полностью интерактивным.
Зачем использовать серверный рендеринг?
Существует три основных причины для создания универсальной версии вашего приложения.
- Содействие веб-сканерам с помощью поисковой оптимизации (SEO)
- Повышение производительности на мобильных и маломощных устройствах
- Показать первую страницу быстро с первой содержательной краской (FCP)
Содействие веб-сканерам (SEO)
Google, Bing, Facebook, Twitter и другие сайты социальных сетей используют сканеры для индексации контента вашего приложения и обеспечения его поиска в сети. Эти веб-сканеры могут быть не в состоянии перемещаться и индексировать ваше высокоинтерактивное приложение Angular, как это может сделать пользователь.
Angular Universal может сгенерировать статическую версию вашего приложения, в которой легко осуществлять поиск, создавать ссылки и выполнять навигацию без JavaScript. Universal также делает предварительный просмотр сайта доступным, поскольку каждый URL возвращает полностью отображаемую страницу.
Повышение производительности на мобильных и маломощных устройствах
Некоторые устройства не поддерживают JavaScript или выполняют его так плохо, что пользовательский интерфейс неприемлем. В этих случаях вам может потребоваться версия приложения, не использующая JavaScript, для сервера. Эта версия, однако ограниченная, может быть единственной практической альтернативой для людей, которые иначе не могли бы использовать приложение вообще.
Показать первую страницу быстро
Быстрое отображение первой страницы может иметь решающее значение для привлечения пользователей. 53 процента посещений мобильных сайтов прекращаются, если загрузка страниц занимает более 3 секунд. Ваше приложение может запускаться быстрее, чтобы привлечь этих пользователей, прежде чем они решат заняться чем-то другим.
С Angular Universal вы можете создавать целевые страницы для приложения, которые выглядят как законченное приложение. Страницы являются чистым HTML и могут отображаться, даже если JavaScript отключен. Страницы не обрабатывают события браузера, но они поддерживают навигацию по сайту с помощью маршрутизатора Link.
На практике вы будете использовать статическую версию целевой страницы, чтобы удерживать внимание пользователя. В то же время вы загрузите полное приложение Angular за ним. Пользователь воспринимает практически мгновенную производительность с целевой страницы и получает полный интерактивный опыт после полной загрузки приложения.
Универсальные веб-серверы
Универсальный веб-сервер отвечает на запросы страниц приложения с помощью статического HTML, отображаемого универсальным механизмом шаблонов. Сервер получает и отвечает на запросы HTTP от клиентов (обычно браузеров) и обслуживает статические ресурсы, такие как сценарии, CSS и изображения. Он может отвечать на запросы данных либо напрямую, либо в качестве прокси для отдельного сервера данных.
Пример веб-сервера для этого руководства основан на популярной платформе Express.
Примечание. Любая технология веб-сервера может обслуживать приложение Universal, если оно может вызывать функцию renderModuleFactory (). Обсуждаемые здесь принципы и моменты принятия решения применимы к любой технологии веб-сервера.
Универсальные приложения используют пакет платформа-сервер Angular (в отличие от платформы-браузера), который предоставляет серверные реализации DOM, XMLHttpRequest и другие низкоуровневые функции, которые не зависят от браузера.
Сервер (Node Express) передает клиентские запросы на страницы приложения в NgUniversal ngExpressEngine. Под капотом это вызывает функцию renderModuleFactory () Universal, обеспечивая при этом кэширование и другие полезные утилиты.
Функция renderModuleFactory () принимает в качестве входных данных шаблонную HTML-страницу (обычно index.html), угловой модуль, содержащий компоненты, и маршрут, который определяет, какие компоненты отображать. Маршрут идет от запроса клиента к серверу.
Каждый запрос приводит к соответствующему представлению для запрошенного маршрута. Функция renderModuleFactory () отображает представление в пределах
Наконец, сервер возвращает визуализированную страницу клиенту.
Работа вокруг API браузера
Поскольку универсальное приложение не выполняется в браузере, на сервере могут отсутствовать некоторые API и возможности браузера.
Например, серверные приложения не могут ссылаться на глобальные объекты только для браузера, такие как окно, документ, навигатор или местоположение.
Angular предоставляет некоторые инъекционные абстракции над этими объектами, такие как Location или DOCUMENT; он может адекватно заменить эти API. Если Angular не предоставляет этого, можно написать новые абстракции, которые делегируют API-интерфейсам браузера в браузере и альтернативной реализации на сервере (иначе говоря, шимминг).
Точно так же, без событий мыши или клавиатуры, приложение на стороне сервера не может полагаться на пользователя, нажимающего кнопку для отображения компонента. Приложение должно определить, что отображать, основываясь только на входящем клиентском запросе. Это хороший аргумент для того, чтобы сделать приложение маршрутизируемым.
Использование абсолютных URL для запросов к серверу
В обычных приложениях Angular запросы отправляются по относительным URL, таким как api / heroes. В универсальном приложении URL-адреса HTTP должны быть абсолютными (например, https://my-server.com/api/heroes). Это означает, что вам нужно изменить свои службы, чтобы они выполняли запросы с абсолютными URL-адресами при работе на сервере и с относительными URL-адресами при запуске в браузере.
Одним из решений является предоставление полного URL-адреса для вашего приложения на сервере и создание перехватчика, который может извлечь это значение и добавить его к URL-адресу запроса. Если вы используете ngExpressEngine, как показано в примере в этом руководстве, половина работы уже сделана. Предположим, что это так, но тривиально предоставить такую же функциональность.
Начните с создания HttpInterceptor:
import {Injectable, Inject, Optional} from '@angular/core';
import {HttpInterceptor, HttpHandler, HttpRequest, HttpHeaders} from '@angular/common/http';
import {Request} from 'express';
import {REQUEST} from '@nguniversal/express-engine/tokens';
@Injectable()
export class UniversalInterceptor implements HttpInterceptor {
constructor(@Optional() @Inject(REQUEST) protected request: Request) {}
intercept(req: HttpRequest, next: HttpHandler) {
let serverReq: HttpRequest = req;
if (this.request) {
let newUrl = `${this.request.protocol}://${this.request.get('host')}`;
if (!req.url.startsWith('/')) {
newUrl +='/';
}
newUrl += req.url;
serverReq = req.clone({url: newUrl});
}
return next.handle(serverReq);
}
}
Демонстрация в реальном времени:
Посмотрите HttpInterceptor Pen от w3resource ( @ w3resource ) на CodePen .
Далее предоставим перехватчик в провайдерах для сервера AppModule (app.server.module.ts):
import {HTTP_INTERCEPTORS} from '@angular/common/http';
import {UniversalInterceptor} from './universal-interceptor';
@NgModule({
...
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: UniversalInterceptor,
multi: true
}],
})
export class AppServerModule {}
Демонстрация в реальном времени:
См. Pen app.server.module.ts от w3resource ( @ w3resource ) в CodePen .
Теперь при каждом HTTP-запросе, сделанном на сервере, этот перехватчик будет запускать и заменять URL-адрес запроса абсолютным URL-адресом, указанным в объекте Express Request.
Универсальный шаблонизатор
Важным битом в файле server.ts является функция ngExpressEngine ().
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
Демонстрация в реальном времени:
Смотрите Pen server.ts от w3resource ( @ w3resource ) на CodePen .
Функция ngExpressEngine () - это оболочка вокруг функции renderModuleFactory () Universal, которая превращает запросы клиента в отображаемые сервером HTML-страницы.
- Первый параметр - AppServerModule. Это мост между универсальным рендерером на стороне сервера и приложением Angular.
- Второй параметр, extraProviders, является необязательным. Это позволяет вам указать поставщиков зависимостей, которые применяются только при работе на этом сервере. Вы можете сделать это, когда вашему приложению нужна информация, которая может быть определена только текущим экземпляром сервера. Одним из примеров может быть источник работающего сервера, который можно использовать для вычисления абсолютных URL-адресов HTTP, если не используется токен запроса, как показано выше.
Функция ngExpressEngine () возвращает обратный вызов Promise, который преобразуется в отображаемую страницу. Двигатель сам решает, что делать с этой страницей. Обратный вызов Promise этого движка возвращает отображаемую страницу на веб-сервер, который затем перенаправляет ее клиенту в ответе HTTP.
Примечание. Эти оболочки помогают скрыть сложность функции renderModuleFactory (). В универсальном репозитории есть больше обёрток для разных бэкэнд-технологий.
Фильтрация URL-адресов запросов
ПРИМЕЧАНИЕ. Базовое поведение, описанное ниже, обрабатывается автоматически при использовании схемы NgUniversal Express, это полезно при попытке понять базовое поведение или воспроизвести его без использования схемы.
Веб-сервер должен отличать запросы страницы приложения от других типов запросов.
Это не так просто, как перехватить запрос на корневой адрес /. Браузер может запросить один из маршрутов приложения, например / dashboard, / heroes или / detail: 12. Фактически, если приложение отображалось только сервером, каждая нажатая ссылка на приложение попадет на сервер в виде URL-адреса навигации, предназначенного для маршрутизатора.
К счастью, у маршрутов приложений есть что-то общее: в их URL отсутствуют расширения файлов. (Запросы данных также не имеют расширений, но их легко распознать, поскольку они всегда начинаются с / api.) Все запросы статических активов имеют расширение файла (например, main.js или /node_modules/zone.js/dist/zone.
Поскольку мы используем маршрутизацию, мы можем легко распознать три типа запросов и обрабатывать их по-разному.
- Запрос данных: запросить URL, который начинается / api.
- Навигация по приложению: запросить URL без расширения файла.
- Статический актив: все остальные запросы.
Сервер Node Express - это конвейер промежуточного программного обеспечения, которое фильтрует и обрабатывает запросы один за другим. Вы настраиваете конвейер сервера Node Express с помощью вызовов app.get (), подобных этому для запросов данных.
// TODO: implement data requests securely
app.get('/api/*', (req, res) => {
res.status(404).send('data requests are not supported');
});
Демонстрация в реальном времени:
См. Pen server.ts (URL-адрес данных) по w3resource ( @ w3resource ) на CodePen .
Примечание. Этот пример сервера не обрабатывает запросы данных.
Модуль учебного веб-API in-memory, инструмент для демонстрации и разработки, перехватывает все HTTP-вызовы и имитирует поведение удаленного сервера данных. На практике вы должны удалить этот модуль и зарегистрировать свое промежуточное ПО веб-API на сервере здесь.
Следующий код фильтрует URL-адреса запросов без расширений и обрабатывает их как запросы навигации.
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('index', { req });
});
Демонстрация в реальном времени:
См. Pen server.ts (навигация) по w3resource ( @ w3resource ) в CodePen .
Безопасное обслуживание статических файлов
Один app.use () обрабатывает все другие URL-адреса как запросы статических ресурсов, таких как JavaScript, изображения и файлы стилей.
Чтобы клиенты могли загружать только те файлы, которые им разрешено просматривать, поместите все файлы ресурсов, относящиеся к клиенту, в папку / dist и выполняйте только запросы на файлы из папки / dist.
Следующий код Node Express направляет все оставшиеся запросы в / dist и возвращает ошибку 404 - НЕ НАЙДЕН, если файл не найден.
// Server static files from /browser
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
Демонстрация в реальном времени:
Смотрите Pen server.ts (статические файлы) от w3resource ( @ w3resource ) на CodePen .
Предыдущий: Обновление с AngularJS до Angular
Далее: Интернационализация (i18n)
Новый контент: Composer: менеджер зависимостей для PHP , R программирования