Дружим Angular с Google

    Дружим Angular с Google


    Google ненавидит SPA


    Когда мы говорим про современные интернет магазины, мы представляем себе тяжелые для понимания серверы, рендрящие тысячи статических страничек. Причем именно эти тысячи отрендеренных страниц одна из причин почему Single Page Applications не прижились в электронной коммерции. Даже крупнейшие магазины электронной коммерции по-прежнему выглядят как куча статических страниц, для пользователя это нескончаемый цикл кликов, ожидания и перезагрузки страниц.


    Одностраничные приложения приятно отличаются динамичностью взаимодействия с пользователем и более сложным UX. Но как не прискорбно обычно пользовательский опыт приносится в жертву SEO оптимизации. Для сеошника сайт на angular это такая себе проблема — поисковикам трудно индексировать страницы с динамическим контентом.


    Другой недостаток SPA — это превью сайта. Например, пользователь только что купил новый телевизор в нашем интернет магазине, и хочет порекомендовать его своим друзьям в соцсетях. Превью ссылки в случае Angular будет выглядеть так:


    site preview


    Мы любим JS и Angular. Мы верим, что классный и удобный UX может быть построен с на этом стеке технологий, и мы можем решить все сопутствующие проблемы. В какой-то момент мы наткнулись на Angular Universal. Это модуль Angular для рендеринга на стороне сервера. Сначала нам показалось что вот оно решение, но радость была преждевременной — и отсутствие больших проектов, с его применением, было доказательством этого. Шесть месяцев назад мы надеялись найти production ready решение, но поняли, что нет больших проектов, написанных на Universal.


    В итоге, мы начали разрабатывать компоненты для интернет-магазина, используя обычный Angular 2, и ждали, когда Universal будет объединен с Angular Core. На данный момент слияния проектов еще не поизошло, и пока не ясно, когда это произойдет (или как итоговый вариант будет совместим с текущей реализацией), однако сам Universal уже перекочевал в github репозиторий Angular.


    Не смотря на эти трудности, наша цель осталась неизменной — создавать классные веб-приложения на Angular с серверным рендерингом для индексации поисковиками. В результате наша команда разработала более 20 универсальных компонентов для быстрой сборки интернет-магазина. Как в итоге это было достигнуто?


    Что такое Angular Universal


    Прежде всего, давайте обсудим, что такое Angular Universal. Когда мы запустим наше приложение на Angular 2 и откроем исходный код, увидим что-то вроде этого:


    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>Angular 2 app</title>
    
      <!-- base url -->
      <base href="/">
    
    <body>
      <app></app>
    </body>
    
    </html>

    Фронтенд фреймворки, такие как Angular, динамически добавляют контент, стили и данные в тег .

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


    Для решения проблем c индексацией Angular Universal дает нам возможность выполнять рендеринг на стороне сервера. Наша страница будет создаваться на «бэкэнд-сервере», написанном на Node.Js, .NET или другом языке, и браузер пользователя получит страницу со всеми привычными тегами в ней -заголовками, мета-тегами и контентом.


    В нашем случае это отлично, потому что краулеры поисковиков очень хорошо подготовлены к индексированию статических веб-страниц. Мы разрабатываем стандартное приложение на Angular, а Universal заботится о серверном рендеринге.


    До этого момента все выглядит не плохо? Возможно, но дьявол скрыт в мелочах, как всегда.


    Итак, мы хотим поделиться с вами подводными камнями, с которыми мы столкнулись на нашем пути.


    Подводные камни Angular Universal


    Не трогайте DOM


    Когда мы начали тестировать компоненты нашего магазина с помощью Universal, нам пришлось потратить некоторое время, чтобы понять, почему наш сервер падает при запуске без вывода серверной страницы. Например, у нас есть компонент Session Flow component, который отслеживает активность пользователя во время сессии (перемещения пользователя, клики, рефферер, информация об устройстве пользователя и т.д.). После поиска информации в issues на GitHub мы поняли, что в Universal нет обертки над DOM.


    DOM на сервере не существует.


    Если вы склонируете этот Angular Universal стартер и откроете browser.module.ts вы увидите, что в массиве providers разработчики Universal предоставляют дваboolean значения:


    providers: [
        { provide: 'isBrowser', useValue: isBrowser },
        { provide: 'isNode', useValue: isNode },
    
        ...
      ]

    Итак, если вы хотите чтобы ренедеринг на сервере работал, вы должны заинжектить в свои компоненты эти переменные и обернуть в проверку среды выполнения фрагменты кода, где непосредственно происходит взаимодействие с DOM. Например, вот так:


    @Injectable()
    export class SessionFlow{
    
        private reffererUrl : string;
    
        constructor(@Inject('isBrowser') private isBrowser){
            if(isBrowser){
                this.reffererUrl = document.referrer;
            }
        }
    }

    Universal автоматически добавляет false, если это сервер, и true, если браузер. Может быть, позже разработчики Universal пересмотрят эту реализацию и нам не придется беспокоиться об этом.


    Если вы хотите активно взаимодействовать с элементами DOM, используйте сервисы Angular API, такие какElementRef, Renderer или ViewContainer.


    Правильный роутинг


    Поскольку сервер отражает наше приложение, у нас была проблема с роутингом.


    Ваш роутинг на клиенте, написанный на Angular, должен соответствовать роутингу на сервере.


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


        [
          { path: '', redirectTo: '/home', pathMatch: 'full' },
          { path: 'products', component: ProductsComponent },
          { path: 'product/:id', component: ProductComponent}
        ]

    Тогда нужно создать файл server.routes.ts с массивом роутов сервера. Корневой маршрут можно не добавлять:


    export const routes: string[] = [
      'products',
      'product/:id'
    ];

    Наконец, добавьте роуты на сервер:


    import { routes } from './server.routes';
    
    ... other server configuration
    
    app.get('/', ngApp);
    routes.forEach(route => {
      app.get(`/${route}`, ngApp);
      app.get(`/${route}/*`, ngApp);
    });

    Пререндеринг стартовой страницы


    Одной из наиболее важных особенностей Angular Universal является пререндеринг. Из исследования Kissmetrics 47% потребителей ожидают, что веб-страница загрузится за 2 секунды или даже менее. Для нас было очень важно отобразить страницу как можно быстрее. Таким образом, пререндеринг в Universal как раз про нашу задачу. Давайте подробнее рассмотрим, что это такое и как его использовать?


    Когда пользователь открывает URL нашего магазина, Universal немедленно возвращает предварительно подготовленную HTML страничку с контентом, а уже затем затем начинает загружать все приложение в фоновом режиме. Как только приложение полностью загрузится, Universal подменяет изначальную страницу нашим приложением. Вы спросите, что будет, если пользователь начнет взаимодействовать со страницей до загрузки приложения? Не беспокойтесь, библиотека Preboot.js запишет все события, которые выполнит пользователь и после загрузки приложения выполнит их уже в приложении.


    Чтобы включить пререндеринг, просто добавьте в конфигурацию сервера preboot: true:


    res.render('index', {
          req,
          res,
          preboot: true,
          baseUrl: '/',
          requestUrl: req.originalUrl,
          originUrl: `http://localhost:${ app.get('port') }`
        });
      });

    Добавление мета-тегов


    В случае SEO-ориентированного сайта добавление мета-тегов очень важно. Например, у нас есть Product component, который представляет страницу определенного продукта. Он должен асинхронно получать данные о продуктах из базы данных и на основе этих данных добавлять мета-теги и другую специальную информацию для поискового робота.


    Команда Angular Universal создала сервис angular2-meta, чтобы легко манипулировать мета-тегами. Вставьте мета-сервис в ваш компонент и несколько строк кода добавлят мета-теги в вашу страницу:


    import { Meta, MetaDefinition } from './../../angular2-meta';
    
    @Component({
      selector: 'main-page',
      templateUrl: './main-page.component.html',
      styleUrls: ['./main-page.component.scss']
    })
    export class MainPageComponent {
    
      constructor(private metaService: Meta){
        const name: MetaDefinition = {
          name: 'application-name',
          content: 'application-content'
        };
        metaService.addTags(name);
      }
    }

    В следующей версии Angular этот сервис будет перемещен в @angular/platform-server


    Кэширование данных


    Angular Universal запускает ваш XHR запрос дважды: один на сервере, а другой при загрузке приложения магазина.


    Но зачем нам нужно запрашивать данные на сервере дважды? PatricJs создал пример, как сделать Http-запрос на сервере один раз и закэшировать полученные данные для клиента. Посмотреть исходный код примера можно здесь. Чтобы использовать его заинжекте Model service и вызовите метод get для выполнения http-вызовов с кешированием:


        public data;
    
        constructor(public model: ModelService) {
            this.universalInit();
        }
    
        universalInit() {
            this.model.get('/data.json').subscribe(data => {
            this.data = data;
            });
        }

    Выводы


    Рендеринг на стороне сервера с помощью Angular Universal позволяет нам
    создавать клиентоориентированные приложения электронной коммерции более не переживая об индексации вашего приложения. Кроме того, функция «prendering» позволяет сразу показать сайт для вашего клиента, улучшая время рендеринга (что довольно неприятный момент для Angular приложений из-за большого размера самой библиотеки).

    Похожие публикации

    Средняя зарплата в IT

    111 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 7 268 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 11

      +2

      Статья несколько устарела, кажется, года на 3-4 (даже ссылки ведут на 404).

        0

        Да, это старая статья, просто обновил картинки и она вылезла вверх как новая.
        Вообще я сегодня https://m.habr.com/ru/post/509390/ публиковал, и эту заодно вернул из черновиков назад. А так да — устарела уже.

        0
          0

          Да, это старая статья, она просто из черновиков. А так мы пошли уже дальше и у нас давно domino используется

            0
            А статьи про это дальше в плане?
              0

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

                0
                В любом случае, куда интереснее почитать что тот более актуальное и удачное, если можно так сказать, в плане реализации.
          0
          Какова все-таки ирония. Поисковик Гугла не работает с их собственным фреймворком из коробки.
            0

            Работает. Гугл индексирует SPA, только делает это медленнее и с некоторыми нюансами, вот вам (и автору статьи) серия про JS SEO от Гугла.

            0

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


            Обычный SPA, в среднем, лучше обычной статики на сервере — особенно в случае коробочного е-комерц. Но хорошая статика с оптимизированного бэка почти всегда лучше SPA, особенно по скорости первой отрисовки. И это безумно важно, почитайте про опыт Amazon. А ещё в комплекте идёт полная совместимость с поисковиками. Минус один: нужно сделать хорошо и улучшить сервис-воркерами, офлайном, оптимизацией и вот этим всем.

              0
              Даже крупнейшие магазины электронной коммерции по-прежнему выглядят как куча статических страниц, для пользователя это нескончаемый цикл кликов, ожидания и перезагрузки страниц.

              что в этом плохого? какая разница где наблюдать спиннер — во вкладке браузера или в UI?

              Мы любим JS и Angular. Мы верим, что классный и удобный UX может быть построен с на этом стеке технологий, и мы можем решить все сопутствующие проблемы.

              Делать интернет-магазин как SPA — это попахивает разводом заказчика на деньги.
              Основная ценность интернет-магазина — это фильтры/поиск и Карточка товара. Правильно свёрстанная семантически и со своим «серверным» урл-ом (название которого должен придумать сео-специалист). Т.е., да, фактически интерент магазин — это набор статических перелинкованных страничек. Что в этом плохого?

              Оставьте SPA для проектов — где основной ценностью является не контент, а взаимодействие и результат этого взаимодействия юзера и продукта (читай там где богатый пользовательский сценарий). Например, онлайн-фотошопы, олайн эксель-таблицы, всякие софт-фоны, игры, конструкторы, личные кабинеты, и т.д.

              Там где основная ценность контент — лендинги, магазины, блоги и т.д. — это всё в первую очередь про индексацию и статику… зачем там spa?

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое