Основным преимуществом Angular 2+ был рендеринг на стороне сервера, но поверьте мне, это намного сложнее, чем кажется.

Универсальный проект Angular содержит ошибки, в нем отсутствует документация и он несовместим с большинством плагинов. Дело в том, что при создании AOT-сборок контекст браузера `window` или` document` отсутствует. Следовательно, любые плагины, использующие это, не будут работать для рендеринга на стороне сервера.

Я был разочарован этим и придумал другое решение.

Весь смысл предварительного рендеринга или рендеринга на стороне сервера заключается в передаче скомпилированного исходного кода HTML поисковым системам или любому, кто от него зависит. Многие поисковые системы и социальные сети имеют своих пользовательских агентов, которые отправляют запросы на ваш сайт с просьбой предоставить исходный код. Если ваш веб-сайт является SPA (одностраничным приложением), только исходный код, который вы отправляете, находится внутри `index.html`, что плохо. Все представления создаются динамически в клиентском браузере, но, поскольку поисковые системы не являются браузерами и не компилируют JavaScript должным образом, они ожидают скомпилированного исходного кода.

Теперь для нормальной публики, какой исходный код они получают, потому что все будет делать их браузер. Поэтому нам нужно отфильтровать публикацию и движки, а затем придумать некоторые механизмы, позволяющие по-разному обрабатывать их запросы. Возможно серверная динамическая версия для публичной и статическая (скомпилированная на сервере) для движков.

Для публики мы будем обслуживать файлы, созданные Angular CLI, в папке dist.

Для движков мы перенаправим запрос на сервер node.js, который обработает запрос в phantom.js, который является безголовым браузером с движком JavaScript, и вернет сгенерированный им исходный HTML-код.

Я использую nginx как статический файловый сервер и обратный прокси. Я рекомендую вам использовать ту же самую альтернативную конфигурацию, основанную на приведенной ниже логике.

#/etc/nginx/conf.d/website.conf
server {
    listen 80;
    server_name website.com www.website.com;
    index index.html index.htm;
    root /usr/share/nginx/website/dist;
    location / {
        add_header Access-Control-Allow-Origin "*";
        try_files $uri @prerender;
    }
    location @prerender {
        set $prerender 0;
        if ($uri ~* "\.(js|css|xml|less|png|jpg|jpeg|gif|pdf|doc|txt|ico|rss|zip|mp3|rar|exe|wmv|doc|avi|ppt|mpg|mpeg|tif|wav|mov|psd|ai|xls|mp4|m4a|swf|dat|dmg|iso|flv|m4v|torrent|ttf|woff|svg|eot|woff2)") {
            set $prerender 0;
        }
        # redirection
        if ($prerender = 1) {
            rewrite .* /http://localhost:8080$request_uri? break;
            proxy_pass http://127.0.0.1:3000;
        }
        if ($prerender = 0) {
            rewrite .* /index.html break;
        }
    }
}
server {
    listen 8080;
    server_name localhost;
    index index.html index.htm;
    root /usr/share/nginx/website/dist;
    location / {
        try_files $uri$args $uri$args/ /index.html;
    }
}

Мы запускаем дополнительный сервер 8080, чтобы возвращать только динамическую версию.

Нам известны пользовательские агенты, используемые общими движками, с помощью которых мы можем определить, кто является общедоступным, а кто движком. Все статические файлы типа (js | css | xml | less | png |…) разрешаются прямо рядом с блоком try_files $ uri.

Когда движок с любым из вышеперечисленных пользовательских агентов отправляет запрос на website.com, этот запрос будет перенаправлен на `« http://127.0.0.1:3000 женщина/xxxx. `. Здесь следует помнить, что при перенаправлении полный URL-адрес будет иметь вид «http://127.0.0.1:3000/ http: // localhost: 8080 / signin `для URL-адреса запроса из` http://website.com/signin `.

Но когда публичный (один без вышеуказанных пользовательских агентов) отправляет запрос, мы отправим им обычную динамическую версию.

Теперь нам нужно создать сервер узла для обработки приложения Angular в phantom js.

// index.js
const express = require(‘express’);
const app = express();
const phantom = require(‘phantom’);
const sleep = require(‘sleep-promise’);
app.use((req, res, next) => {
    (async function(){
        const instance = await phantom.create(
            [‘ — load-images=no’]
        );
        const page = await instance.createPage();
        await page.on(‘onResourceRequested’, (requestData) =>    console.log(‘Requesting: ‘, requestData.url));
        await page.on(‘onError’, err => console.log(‘Error: ‘, err));
        const status = await page.open(req.originalUrl.slice(1));
        await sleep(5000); // wait 5 seconds to render HTML
        const content = await page.property(‘content’);
        await instance.exit();
        // send response
        res.send(content);
    })();
});
// run app on port 3000
app.listen(3000, () => {
    console.log(‘Prerender listening app on port 3000!’);
});

Выше приведен простой скрипт node.js для запуска экспресс-сервера, который может обрабатывать входящий запрос в phantom.js через порт 3000.

Кеш

Используйте модуль узла apicache для кеширования ответа.

const apiCache = require(‘apicache’);
let cache = apiCache.middleware;
app.use(cache('1 week'));

Контрольная работа

Чтобы проверить это, просто откройте свой инструмент разработчика Chrome и нажмите на три вертикальные точки в правом верхнем углу. Выберите дополнительные инструменты и условия сети. В раскрывающемся списке агента пользователя выберите бот Google. Теперь, когда вы запросите http: // website.com, вы получите скомпилированный исходный код. Также проверьте view-`source: http: // website .com`