Одностраничные сайты или Single Page Applications (SPA) – это круто. Главный их профит в том, что SPA быстрее и отзывчивее на действия пользователей. Достигается это за счет переноса логики работы на клиентскую сторону и активного взаимодействия с сервером посредством ajax.

Бытует мнение, что SPA – это мощные приложения на ангуляре или реакте, ворочающие тоннами данных в какой-нибудь панели управления или в сложном сервисе. И в целом это так. Но я убежден, что есть смысл писать одностраничные приложения не только для таких сервисов, но и для обычных корпоративных сайтов-визиток.

Зачем это надо и как это сделать, приложив немного усилий? Об этом ниже. Поехали.

Итак, зачем это баловство?

Самое главное – это скорость работы.

Конечно, при разработке одностраничного сайта-визитки мы столкнемся с некоторыми проблемами:

  • 1. Как подступиться, с чего начать?
  • 2. Как разобраться с историей браузера, с History API?
  • 3. Какой фреймворк или библиотеку изучить: ангуляр, реакт? А мы ни одного не знаем.
  • 4. Как заставить поисковики индексировать одностраничный сайт?

Ответы на эти вопросы:

  • 1. Разберемся в этой же статье, на примере простого сайта
  • 2. Тоже расскажу ниже, это десяток строк кода
  • 3. Никакой, обойдемся нативным javascript-ом и jQuery в качестве помощника
  • 4. Про поисковики будет следующая статья из этой серии

Почему без ангуляра-реакта?
Конечно же, это очень нужные темы, рекомендую их изучать. Но для нашего примера они не понадобятся, нам достаточно обладать минимальными знаниями javascript-a.

Одностраничники – это тема не одной статьи. Это будет целый проект, серия статей минимум из трех штук. И затрагиваться в нем будут самые разные темы. Но уточню. Мы будем строить не сложную админку со взаимодействием с сервером посредством REST API (по крайней мере, в самом начале). В первых уроках наш одностраничный сайт будет обычной визиткой из десятка страниц. Но сайт будет работать без перезагрузки страниц и радовать наших пользователей скоростью и отзывчивостью интерфейса.

Идея сайта, как он устроен

Возьмем самый обычный корпоративный сайт: главная страница, раздел "О проекте", контакты и блог. В разделе "Блог" будет несколько ссылок на внутренние страницы. На каждой странице забьем какой-нибудь контент и вставим немного перекрестных ссылок.

На каждой странице сайта, как правило, есть повторяющийся контент. У нас это будет шапка и меню. И есть основное содержимое страницы, которое меняется. Мы сделаем так: загрузим страницу всего один раз, а потом кликая по ссылкам, будем динамически подгружать нужное содержимое аяксом. При этом мы будем менять заголовок страницы во вкладке браузера, url в адресной строке и запоминать историю в браузере, чтобы работала навигация через кнопки Назад/Вперед браузера.

Контент для каждой отдельной странице будет храниться в отдельном html-файле.

Можете сразу посмотреть, что у нас в итоге получится – Демо-сайт

Структура сайта и подготовительные работы

Структура файлов-папок такова. В корне проекта файл index.php и .htaccess. Почему именно php, а не html, расскажу позже. В папке css лежат стили в файле main.css. В папке js – библиотека jquery.js и главный файл приложения main.js. В папке pages лежат html-файлы с содержимым сайта – на каждую страницу по одному файлу.

Готовим контент

Я сделаю демо-сайт на примере своего проекта webdevkin. Набор страниц будет таким:

  • — main – Главная
  • — about – О проекте
  • — blog – Блог
  • — shop – Интернет-магазины
  • — frontend – Фронтенд
  • — mysql – База данных MySql
  • — widgets – Встраиваемые виджеты

Соответственно, в папке pages будут лежать 9 html-файлов. Всю разметку для контента найдете в исходниках. Для примера приведу содержимое только одного файла – simpple.html

Как видно, никаких head, body, html, script здесь нет – только разметка, относящаяся к конкретной странице.

index.php и .htaccess

Почему не index.html?
Дело в том, что на нашем сайте будет одна-единственная физическая страница – index. Но нас интересуют и такие адреса, как site.ru/about, site.ru/contacts и прочее. Но страниц about и contacts в корне нашего сайта нет. Папка pages с набором html-файлов – это не полноценные страницы, а просто куски html-кода, которые встраиваются внутрь общего каркаса.

Поэтому, чтобы при обращении к site.ru/about не посыпались 500, 403 и еще бог знает какие ошибки, мы должны все входящие запросы на сайт перенаправлять на index.php, который уже и будет эти запросы разруливать. Впрочем, пока что index.php у нас – это обычная html-разметка без единой строчки php-кода (но это только пока). А в .htaccess мы пропишем следующее. Возвращаться к нему придется редко.

В тему
Однажды я писал статью про Простой RESTful-сервис на нативном PHP. Там Вы найдете немного больше информации про такой способ перенаправления запросов.

html-код будет у нас очень простым. В head подключаются стили css/main.css. В подвале 2 js-файла: js/jquery.js и js/main.js. А в body будет следующее:

Сначала выводим меню. Дальше идет заголовок страницы. И ниже пустой div с – парсер когда-нибудь выбесит и я его заменю). В #content будут подгружаться динамически содержимое страниц из файлов pages/*.html. Пока ничего интересного.

Только обратите внимание на атрибуты data-menu и data-link="ajax" у ссылок. Они введены для того, чтобы отличать ссылки-навигации от обычных внешних ссылок, которые на нашем сайте тоже будут. data-link="ajax" означает, что при клике по этой ссылке мы перехватим стандартное поведение браузера и возьмем работу со ссылкой в свои руки. А data-menu означает, какой пункт главного меню будет выделен при клике на оную ссылку. Здесь data-menu дублируется с атрибутом href, но возможны и другие варианты. Например, когда мы залезем в раздел frontend блога, то мы укажем data-menu="blog".

2 примера для наглядности:

Это ссылка на страницу site.ru/simpple, при переходе на которую содержимое страницы подгрузится из папки pages/simpple.html, и при этом будет выделен пункт главного меню "simpple"

А это обычная внешняя ссылка, кликнув на которую мы отправимся на внешний ресурс.

Стили main.css

Быстро пролистаем и скопипастим самое скучное – файл css/main.css.

А вот теперь самое интересное – javascript-код, который превратит наш набор отдельных файлов в одностраничный сайт.

javascript. Общий код и конфиги

Зададим каркас js-кода.

Мы имеем отдельный модуль app, который при загрузке страницы запускает свою функцию init. В ней навешиваем обработчики событий и выполняем еще некоторый код. Также видим 2 объекта: config и ui. В ui будут закэшированы все dom-элементы, которые понадобятся нам в работе.

$menu нам нужно, чтобы выделять отдельные пункты меню, $pageTitle будем менять динамически при переходе между страницами, а в $content будет подгружаться содержимое файлов pages/*.html

А вот config выглядит интереснее.

siteTitle: ‘Webdevkin SPA’ – заголовок сайта, используется в нескольких местах.
mainPage: ‘main’ – указываем стартовую страницу сайта, ту, которая откроется при заходе на site.ru. Сейчас это главная страница main, но Вам запросто может прийти в голову поставить стартовую, например, "О проекте" – about.

Самое важное во всем конфиге – это объект pages. Каждое поле объекта описывает одну страницу сайта. Сейчас нам понадобятся только 2 пункта: заголовок страницы и меню, к которому оная страница относится. Ключи объекта, то есть страницы, совпадают с названиями файлов в pages/*.html и атрибутами href во внутренних ссылках.

И наконец, напишем код, ради которого все и затеяли. Возможно, удивитесь, но кода, выполняющего непосредственно работу по обслуживанию сайта, намного меньше, чем подготовка к его написанию.

Подгрузка контента с помощью ajax. History API

Пойдем по порядку. Займемся функцией init, в которой есть привязывание нужных событий _bindHandlers

В первой строке мы отлавливаем клики на внутренние ссылки a[data-link="ajax"] и отправляем их в функцию _navigate. Теперь мы окончательно убедились, что нужны отдельные атрибуты data-link="ajax" 🙂

Далее window.onpopstate = _popState;
Из документации.
Событие popstate отсылается объекту window каждый раз, когда активная запись истории меняется между двумя записями истории для одного и того же документа.
Проще говоря, это срабатывание кнопок Назад/Вперед в браузере. Как мы видим, нативное поведение браузера тоже нужно перехватывать. Поэтому мы отдадим управление функции _popState.

Для лучшего восприятия приведу код сразу для обеих функций

При явном клике по ссылке _navigate мы останавливаем всплытие события клика и отменяем дефолтное поведение браузера (переход по ссылке). Затем мы определяем страницу, которую мы хотим загрузить (понимаем по атрибуту href), и вызываем новую функцию _loadPage. Она и сделает всю основную работу по загрузке контента, изменению заголовка и прочее-прочее. И в конце через history.pushState добавляем новую запись в истории браузера. Да, мы сами, явным образом создаем историю браузера. Тогда, когда считаем нужным. И сохраняем данные о загружаемой странице в объект . Эти данные нам пригодятся в следующей функции _popState.

В _popState идея аналогична: ищем нужную страницу и запускаем ту же _loadPage.

e.state && e.state.page – вытаскивает нам страницу из объекта истории, которую мы предусмотрительно записали в _navigate. Если же объект e.state недоступен (например, когда мы первый раз зашли на сайт site.ru и еще не успели побродить по нему), то берем страницу, указанную главной в нашем конфиге – config.mainPage.
По совести говоря, функция history.pushState и тот факт, что в window.onpopstate доступен объект e.state с данными, записанными в pushState, – это все, что нам достаточно знать о History API. Для более любопытных товарищей, не сомневаюсь, что гугление поможет найти и другие хорошие способы работы с историей браузера.

Мы же глубокими изысканиями заниматься не будем, а напишем код главной функции _loadPage

Выглядит он вполне заурядно. Сначала формируем url, то есть путь, по которому мы загрузим html для страницы. Потом вытаскиваем из конфига заголовок страницы и пункт меню, подлежащий выделению. Далее получаем html-содержимое страницы через банальный jQuery.get и выполняем еще ряд нехитрых действий.

а) Обновляем заголовок страницы
б) В 2 строки выделяем нужный пункт меню
в) Меняем заголовок уже на самой странице, в html-коде
г) Загружаем собственно html-содержимое страницы, полученное из url

Почти все! Остался маленький момент. Если мы сейчас перейдем, например, на страницу site.ru/blog и обновим ее, то загрузится не блог, а пустая страница. Потому что при инициализации мы _loadPage не вызываем. Чтобы это исправить, нам нужно дописать пару строк в функцию init, которая в итоге будет выглядеть так

Вот теперь точно все!

Подведем итоги и напомним ссылки

Итак, мы написали простой, но вполне рабочий одностраничный сайт, а также немного узнали, как работает History API. Честно говоря, чувствую себя режиссером плохого фильма, когда 80% времени ведут к какой-то грандиозной развязке, а в конце все оказывается намного проще, чем ожидалось.

С одной стороны, почти весь код – это просто подготовка, обвязка, написание конфигов и прочих скучных вещей. А действительно полезный код, выполняющий непосредственную работу, занимает 2 десятка строк.

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

Есть и третья сторона. Для не знакомых с одностраничными сайтами – это ни фига не простая вещь. И я думаю, это здорово, когда изначально кажущаяся сложной тема, при подробном рассмотрении оказывается вполне себе решаемой, причем без великих усилий с нашей стороны.

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

Введение

Динамические веб приложения (SPA, Single Page Applications) становятся все более популярными. Их использование позволяет упростить и ускорить разработку и поддержку за счет следующий преимуществ:

  • не требуется server-side рендеринг;
  • сайт — это набор статических файлов (html, js, css, images, fonts), которые лежат на CDN;
  • нет затрат на масштабирование frontend;
  • код frontend локализован в одном проекте. (Раньше часть кода была на server-side, часть в frontend.);
  • уменьшение затрат на разработку backend (только API);
  • уменьшение нагрузки на backend (только API запросы);
  • возможность вместо собственного backend использовать сервисы типа (https://scaphold.io), микросервисы, lambda.

Из популярных библиотек для SPA хочется отдельно выделить ReactJS. Его использование позволяет создавать сложные веб приложения быстро и просто, сохранить скорость разработки на большой базе кода, избежать регрессионных ошибок при добавлении новых функций.

Большой нерешенной проблемой SPA является их полная несовместимость с веб краулерами поисковых систем (индексация сайтов) и социальных сетей (sharing в соц. сетях). На текущий момент краулеры не выполняют на своей стороне javascript и поэтому не могут получить содержимое SPA в виде html кода. В 2015 году Google официально отказался от поддержки выполнения javascript кода на своей стороне.

Решения

Сегодня есть несколько типов решений для проектов, которые хотят полноценно разрабатывать SPA.

Поддержка собственного server-side рендеринга

Что это такое

Это старый подход к организации веб приложения при котором сохраняется server-side рендеринг html страниц. Когда загружается SPA в браузере клиента, обычно, сначала приложение удаляет весь html код, созданный сервером, и его место заполняется результатом работы программы. На текущий момент это наиболее часто используемое решение.

Чем это хорошо

Чем это плохо

Изоморфный рендеринг

Что это такое

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

Чем это хорошо

Чем это плохо

Prerender.io

Что это такое

Это сервис с открытым исходным кодом, который позволяет организовать перенаправление трафика от краулеров на специальный браузер, который выполняет javascript код и вернет статическую html страницу. В качестве браузера используется PhantomJS.

Чем это хорошо

Чем это плохо

Способы интеграции

Предлагает множество однотипных интеграций для разных фрейморков и веб серверов. Они заключаются в проксировании трафика от краулеров на prerender.io (если в заголовке запросов UserAgent соответствует краулеру или в параметрах запроса есть _escaped_fragment_ ). Остальной трафик отдается в обычном режиме (набор статики).

Renderjs.io

Что это такое

Cloud based сервис, который работает аналогично prerender.io, но работает быстро, не использует cache, использует последнюю версию Chromium.

Как это работает

После первой загрузки SPA приложения в современных браузерах работают очень быстро. Для отрисовки новой страницы обычно требуется всего несколько запросов. Renderjs.io — сервис, который делает это для браузеров. Для рендеринга используется Chromium browser. При первом обращении краулера в настоящем, реальном Chromium открывается закладка с веб приложением. Эта закладка не закрывается долгое время. При обращении по новому url не происходит полной загрузки всего, а лишь pushState в уже загруженное приложение. Таким образом рендеринг работает очень быстро и очень легко отлаживать, если у вас в Chromium работает — работает и на Renderjs.io.

Чем это хорошо

Чем это плохо

Способы интеграции

Интеграция при помощи middleware аналогична prerender.io.

Интеграция при помощи DNS записи. При этом способе интеграции проект перенаправляет весь свой трафик на cdn.renderjs.io и получает полноценную обработку трафика от краулеров через рендер сервис, а остальной трафик на свой веб сервис. Этот способ позволяет небольшим проектам за 5 минут сделать свой SPA доступным для поисковых систем и заниматься более важными делами.

Выполните несколько простых шагов:

  1. Зарегистрируйтесь;
  2. Создайте новый проект;
  3. Заполните поля. Domain — это поле название домента (mysite.com), с которого будут приходить запросы, а поле origin — это url (обычно хостера mysite.github.io) по которому лежит ваше приложение;
  4. Создайте на вашем DNS сервисе запись CNAME mysite.com -> cdn.renderjs.io;
  5. Проверьте ваше приложение (в терминале curl http://mysite.com или в браузере view-source:http://mysite.com/?_escaped_fragment_= );
  6. Если все правильно, получите отрендеренный html.

Частые вопросы

Q: А почему не отдавать всем клиентам готовый html код?
A: По нескольким причинам:

    без поддержки javascript клиент все-равно не сможет полноценно пользоваться приложением, так что лучше отдавать какой-нибудь fallback.html с надписью включите javascript;

рендеринг требует времени — это все-таки дольше, чем получить набор статики (html, js, css) с cdn.

Q: Как индексировать закрытые разделы, которые требуют cookies?
A: Технически это возможно, но на практике не требуется.

Выводы

Мы считаем, что для молодых проектов на начальном уровне использование renderjs.io и DNS интеграции — идеальное решение, которое позволяет одновременно получить все преимущества полноценного SPA и сосредоточиться на решении действительно важных задач, а именно создании ценности для клиента.

Новые проекты с трафиком тоже могут использовать renderjs.io с DNS интеграцией или nginx интеграцией.

Большие, успешные проекты пусть думают самостоятельно. Если есть желание бороться проблемами, которые уже решены, что ж они большие — сами знают, что делать.

О нас

purelab.io — Мы команда профессиональных разработчиков. Специализируемся на разработке высококачественных и современных веб приложений на базе ReactJS. Используем функциональное программирование. Имеем собственные решения для того, что бы делать вещи правильно. Можем помочь, проконсультировать, разработать. Пишите, будем рады.

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

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

Из популярных фреймворков для SPA хочется отдельно выделить ReactJS.

Почему вы шаблонизатор называете фреймворком?

Relay забыли и GraphQL, для полноты картины.
Подразумевать могут что угодно, а по факту ReactJS это View библиотека. Нужно называть вещи своими именами, иначе все будут говорить на разных языках.

Чаще всего ЦементЖС подразумевает связку Цемент+Железо+Вода. Чем не каркас здания?

Сколько нужно времени, чтобы просто вывести на экран большой список, используя современные фреймворки?

О том и речь, что когда говорят "у нас проект на Реакте" — никогда не знаешь звязка каких именно библиотек имеется ввиду и в какую архитектуру они связаны.

Изоморфный рендеринг
Чем это плохо
не работает на проектах сложнее «hello world»

Это не правда, работает и на приложениях сложнее Hello world

… работы с динамически загружаемым контентом, управлением очередями запросов, websocket подписками на обновления данных;

Если эти вещи мешают рендерить где бы то ни было, значит у вас проблема с архитектурой приложения, потому что все эти вещи прямого отношения к рендерингу как таковому не имеют. Рендерится HTML код, с пустым состоянием что на фронте что на бэке. А потом это состояние заполняется из хранилища (store). И потом уже это все отдается клиенту. То как вы заполните хранилище, это уже другой вопрос. На бэке вы как правило генерится заполненый store и через props передается полное состояние всего приложения, потом это все отдается на фронт. Да и вообще весь этот изоморфный рендеринг по сути ни чем не отличается от классической схемы применяемой в web разработке хз с каких времен. Поменялись только языки, библиотеки и некоторые принципы проектирования. Но все это базируется на технологиях существующих десятилетиями, поэтому концептуально с точки зрения рендеринга PHP + Smarty так или иначе равно Node.js + ReactJS. Просто и в первом и во втором случае возникают немного разные проблемы. Но это ни как не влияет на все те технологии поверх которых все это работает.

В Renderjs.io в минусы первым пунктом жирным шрифтом нужно записать:

Трудности с изоморфным рендерингом у нас возникали начиная от отсутствия совместимой с браузером версии XMLHttpRequest в nodejs, необходимостью адаптироваться к jsdom (там много чего нет), необходимостью
эмулировать то, что есть в браузере, например определение гео позиции и т.д.

У нас такая схема, вьюшки, которые попадают во viewport браузера клиента рендерят заглушки и подписываются на данные, когда поступают данные — реакт заменяет заглушки данными. Пробовали изоморфно — просто не получилось. Возможно, вы более удачливы.

В любом случае, спасибо за комментарий.

Cloud based — возможно, как минус. Однако для маленьких проектов, прототипов, бережливых стартапов и т.д. это скорее плюс. Я бы не хотел делать то, что уже сделано. Но это мое мнение.

В последнее время в работе специалистов Netpeak Agency много сайтов, использующих AJAX-технологии , а также сайтов написанных на JavaScript фреймворках. Без дополнительной оптимизации они не готовы к продвижению в поисковых системах. Поэтому я детально опишу, что такое SPA-сайты, как они работают, и как можно сделать Single Page Application SEO-Friendly.

Статья будет полезна SEO-специалистам и владельцам сайтов, которые хотят перейти на Single Page Application, но не решаются, потому что SPA могут «поссориться» с поисковыми системами.

Что такое SPA

SPA (Single Page Application) — одностраничное JavaScript приложение, которое запускается и работает в браузере. В отличии от «традиционного» сайта, архитектура на SPA-сайтах построена так, что рендеринг страницы полностью происходит на стороне клиента, а не на стороне сервера.

В браузере пользователя запускается JavaScript-приложение, а все необходимое содержимое страниц динамически загружается с помощью AJAX. Навигация по сайту происходит без перезагрузки страниц. За счет такой архитектуры, SPA-сайты работают быстрее, чем «традиционные» сайты.

Рассмотрим детальнее, как происходит загрузка и рендеринг содержимого на SPA-сайтах:

  1. Пользователь запрашивает HTML содержимое сайта.
  2. В ответ приходит JavaScript-приложение.
  3. Приложение определяет, на какой странице находится пользователь, и какое содержимое ему нужно отобразить.
  4. С помощью AJAX пользователь получает необходимый контент: CSS, JS, HTML и текстовый контент.
  5. JavaScript-приложение обрабатывает полученные данные и отображает их в браузере.
  6. При навигации по сайту обновляется не вся страница, а только необходимое содержимое.
  • высокая скорость работы;
  • быстрая разработка;
  • создание версий для разных платформ на основе готового кода (desktop и mobile приложения).
  • JavaScript не обрабатывается большинством поисковых систем;
  • SPA-сайты не работают без включенного JS в браузере;
  • их нельзя анализировать на предмет ошибок популярными программами и инструментами (например, Netpeak Spider).

Популярные JavaScript фреймворки:

Какие поисковые системы индексируют SPA-сайты

На сегодняшний день только поисковый робот Google умеет рендерить содержимое SPA-сайтов, так как использует для рендеринга инструменты на базе Chrome 41. ASK.com использует выдачу Google. Для остальных поисковых систем необходимо наличие контента в коде в формате HTML.

Оптимизация индексации SPA-сайтов

Роботы Google и Yandex могут проиндексировать Single Page Application, если структура сайта соответствует определенным правилам. При этом, для Яндекса необходимо обязательное наличие полной HTML-копии страницы.

Для Google нужно использовать только правильный формат URL. Летом 2018 Google перестанет индексировать HTML-копии страниц, а будет использовать только рендеринг.

Существует два способа «заставить» поисковых роботов индексировать AJAX-страницы:

  1. Использовать «?_escaped_fragment_=».
  2. Отдавать роботу HTML-копии определяя его по user-agent.

Использование «?_escaped_fragment_=»

Этот способ подразумевает генерацию HTML-копий страниц (снимков) по отдельному URL с использованием параметра «?_escaped_fragment_».

Использование URL c «#!»

Если адреса AJAX страниц формируются с помощью «#» (хеш), значит нужно заменить их на «#!» (хешбенг). Например, с https://example.com/#url на https://example.com/#!url.

Google просканирует содержимое по основному URL, а робот Яндекса, обнаружив в URL «#!», запросит снимок страницы. Он заменит «#!» на «?_escaped_fragment_=» и просканирует ее по адресу https://example.com/?_escaped_fragment_=url.

Примеры адресов c «#!» и HTML-копий страниц:

  • https://example.com/home#!page → https://example.com/home?_escaped_fragment_=page;
  • https://example.com/index/#!main → https://example.com/index/?_escaped_fragment_=main;
  • https://example.com/#!home/index → https://example.com/?_escaped_fragment_=home/index.

Использование «традиционных» URL

Если на сайте используются «традиционные» URL (https://example.com/url), необходимо указать на всех страницах мета-тег:

Google просканирует содержимое по основному URL, а робот Яндекса, обнаружив на странице мета-тег , запросит HTML-копию страницы. Также добавит в URL параметр «?_escaped_fragment_=» и просканирует ее по адресу https://example.com/url?_escaped_fragment_=

Для популярных фреймворков есть готовые решения, которые заменяют «#!» на «традиционные» URL, например, HTML5 mode для Angular.

(!) В HTML-копии документа мета-тег размещать не следует: в этом случае страница не будет проиндексирована.

(!) На страницах HTML-копий canonical должен либо отсутствовать, либо вести сам на себя.

Например, на странице https://example.com/home?_escaped_fragment_= должен быть указан следующий canonical:

Отдавать HTML-копии страницы по основному URL

Этот способ подходит для SPA-сайта с «традиционными» URL. В чем суть: поисковый робот, запрашивая страницу по основному URL, вместо динамической версии получает HTML-копию страницы.

Определить поискового робота можно по User-Agent. К примеру, по списку роботов Яндекса.

Рендеринг HTML-копий

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

HTML-копии — это отрендеренные версии страниц SPA-сайта. Например, содержимое исходного кода страницы SPA-сайта выглядит так:

А вот отрендеренная HTML-копия этой страницы:


Для создания HTML-копий страниц можно:

  1. Использовать рендеринг на своих серверах.
  2. Использовать рендеринг на аутсорсе.
  3. Использовать изоморфный JavaScript.

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

Рендеринг на своих серверах

Подходит для крупных проектов. Для рендеринга HTML-копий на своих серверах можно использовать следующие инструменты:

Рендеринг на аутсорсе

Подходит для небольших проектов. Для рендеринга HTML-копий на сторонних серверах можно использовать такие инструменты:

Изоморфный JavaScript

Изоморфный JavaScript позволяет выполнять рендеринг страницы как на сервере, так и в браузере пользователя. Таким образом, при первой загрузке страницы пользователь получает обычную HTML-страницу и JavaScript-приложение. Дальнейшая навигация по сайту для пользователя будет динамической, в то время как поисковые системы каждый раз будут получать HTML-версию страницы.

Такой подход к разработке сайта позволяет убить сразу двух зайцев: сайт получает все преимущества SPA-сайта и возможность продвижения в поисковых системах без дополнительных внедрений.

Обработка 404 страниц

Рендеринг страницы на SPA-сайтах происходит на стороне клиента, поэтому без дополнительных доработок код ответа несуществующей страницы будет 200 ОК.

SPA-сайты должны корректно обрабатывать несуществующие страницы и отдавать в качестве заголовка страницы 404 ошибку.

Настройка Google Analytics

Стандартный код Universal Analytics выполняется при загрузке каждой новой страницы, а SPA-сайты подгружают контент динамически, «имитируя» переход между страницами. Для того, чтобы Google Analytics корректно обрабатывал переходы на страницах, необходимо сделать так, чтобы счетчик Universal Analytics активировался каждый раз, когда на сайте меняется URL.

Настроить Google Analytics для SPA-сайтов можно, используя Tag Manager и триггер «History» или — передавая данные вручную.

С помощью Tag Manager и триггера «History»

Использование HTML5 History API на SPA-сайтах позволяет настроить корректную работу счетчика Google Analytics в GTM с помощью триггера «History». Каждый раз при обновлении истории (смены URL, Title), тег Google Analytics будет активироваться повторно.

Для этого необходимо в настройках тега в качестве триггера указать дополнительный триггер:

Передача данных вручную

Каждый раз, когда в строке браузера меняется URL, необходимо передавать об этом информацию в Google Analytics.

Чтобы обновить счетчик, нужно использовать команду «set» и указывать новое значение переменной «page»:

Теперь все последующие обращения будут привязаны к новому URL. Чтобы передать информацию о просмотре страницы, нужно использовать команду «send» и указывать значение «pageview» сразу после обновления счетчика:

Выводы

SPA-сайты — это настоящее и будущее, поэтому не нужно бояться брать в работу такие проекты. Single Page Application можно «подружить» с поисковыми системами. Чтобы ваш SPA-сайт был дружелюбен как к SEO, так и к пользователям я рекомендую:

  1. Использовать «традиционные» URL.
  2. Использовать изоморфные приложения или рендеринг HTML-копий.
  3. Настроить правильную отдачу заголовка «404 Not Found».
  4. Настроить корректную работу Google Analytics.