SEO-оптимизация сайта на React или как добиться конверсии от поисковиков если у вас Single Page Application

Nastya17love

Новичок
5 Янв 2021
6
3
Для того, чтобы поисковые роботы “увидели” страницы сайта, им необходимо иметь возможность прочесть контент страницы, в том числе содержащий ссылки на другие страницы (при этом подход с sitemap я тут описывать не буду, но упомянул, на случай если кому-то станет интересно, можете погуглить. Мы пока обходимся без карт). Из этого вытекает, что поисковой робот всё же должен “по старинке” загрузить html-разметку, содержащую контент страницы с web-сервера. Ну и конечно же, каждая из страниц, запрашиваемых с web-сервера, должна отдавать код 200 (случаи с необходимостью переадресаций с кодом 301 я тут также рассматривать не буду) и приходить в виде стандартного html-документа, содержащего текстовый и медиа-контент данной страницы, а так же ссылки на другие страницы и, конечно же, необходимые для SEO-оптимизации элементы, такие как ряд обязательных meta-тегов, заголовков и так далее. Общий список необходимого “SEO-тюнинга” любого веб-ресурса достаточно велик и про него можно написать отдельный материал и не один. Затронем тут обязательный “план минимум”, который включит в себя следующие пункты:

1 - Каждая из страниц ресурса должна в блоке <head> включать в себя:

Meta-тег title (заголовок страницы)

Meta-тег description (описание страницы)

Meta-тег keywords (перечень ключевых фраз)

2 - Каждая страница должна иметь в блоке <body> основной заголовок внутри html-элемента <h1> расположенный как можно выше перед началом текстового контента.

3 - Каждое изображение, которое присутствует на странице в виде html-элемента <img>, должно иметь атрибут alt, описывающий содержимое данного изображения.

Ну и конечно же, на сайте не должно быть “битых” ссылок отдающих с web-сервера код ошибки 404 (либо иной) или какой-либо пустой контент вместо ожидаемого.

И тут снова вспоминаем, что у нас SPA (Single Page Application) и с backend-а приходит лишь пустая часть разметки html-документа, включающая в себя информацию для загрузки js и css кода, который после загрузки и выполнения отрисует нам контент запрошенной страницы.

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

Подбор решения и реализация
Что же делать, если у Вас уже есть готовый SPA, либо просто отточенный архитектурный подход, нарушать который ради SEO было бы кощунством?

Ответ: Реализация пререндеринга, как конечного шага сборки приложения. Пререндеринг – это процесс выполнения js-кода после его основной сборки и сохранение получившихся html-копий отрисованных страниц, которые в последствии и будут отдаваться с web-сервера при соответствующих запросах.

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

Конечно есть такие решения как Next.Js и ему подобные (а также другие способы реализации того же SSR). И я не могу не упомянуть об этом. Но изучив их, я пришел к выводу, что они вносят ряд ограничений (либо усложнений) в процесс конфигурации основной сборки и имеют крайне заметное влияние на архитектуру приложения. Также, есть сторонние внешние сервисы пререндеринга, но для закрытой экосистемы банка – лишний внешний инструмент был бы крайне нежелателен (и опять же, зачем перекладывать зону ответственности на внешний инструмент, если можно реализовать его функционал внутри проекта).

После анализа, я пришел к максимально подходящему нам инструменту для покрытия описанных кейсов, реализующему проход по страницам SPA на React (и не только) и создание html-копий страниц.

Выбор пал на React-Snap.

Если в двух словах, то данный инструмент позволяет после сборки осуществить запуск Chromium-а и передать ему получившееся приложение. Затем пройти по всем его страницам, используя пул найденных ссылок так, как сделал бы это поисковой робот. При этом html-копии отрисованных средствами js страниц будут сохранены в выбранный каталог с сохранением иерархии путей, из которых они были получены относительно корня проекта.

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

Установка React-Snap не вызывает никаких дополнительных вопросов, так как его пакет доступен для скачивания стандартным образом из npm (и yarn).

Конфигурация запуска React-Snap описывается в корневом файле package.json проекта. Давайте рассмотрим пример минимальной конфигурации:

"scripts": {
// …Необходимые команды запуска hot-а, сборки и т.п.
"build:production": "webpack --mode production && react-snap"
// …Другие необходимые команды
},
"reactSnap": {
"source": "dist", // Каталог собранного приложения
"destination": "dist", // Каталог для сохранения html-копий
"include": [ // Список энтрипоинтов для обхода страниц
"/",
"/404",
"/500"
// …Другие необходимые энтрипоинты
]
}
Что касается блока ”include”, его желательно описать, иначе, например, html-копия для странички ошибки (500, либо другая техническая страница, при наличии) не будет создана, так как не на одной из страниц сайта не фигурирует ни одной ссылки на нее. Как следствие, React-Snap не узнает о её наличии. В теории и поисковик на них ходить не должен, но бывают случаи, когда страница создается для распространения ссылки за пределами сайта, а на сайте на нее ссылки может и не быть (к примеру, баннеры для рекламных компаний и тому подобное). Это как раз тот самый случай. Тут стоит проанализировать, нет ли у вас еще каких-то (возможно аналогичных «технических») страничек, на которые прямые ссылки на сайте отсутствуют.

Далее, для нормальной работы самого React-приложения у конечного пользователя, “поверх” DOM который придёт с web-сервера, нам потребуется внести небольшую правку в корневой render:

import { hydrate, render } from "react-dom";
// … Ваш код
const rootElement = document.getElementById("root"); // (или ваш id при олтличии)
if (rootElement.hasChildNodes()) { // …Если в корневом элементе есть контент, то…
hydrate(<App />, rootElement); // …"цепляем" приложение на существующий DOM.
} else { // …Иначе рендерим приложение стандартным образом
render(<App />, rootElement);
}
Вот и всё что нам потребуется для начала (а возможно в ряде случаев и в принципе всё что необходимо будет сделать). Уже можно попробовать выполнить build с созданием html-копий страниц вашего ресурса. На выходе (с приведенным выше конфигом) в каталоге /dist вы получите тот же набор js и css файлов (а также других ресурсов), что и ранее, index.html, плюс файл 200.html и другие html-файлы с копиями контента ваших страниц.

Ранее у вас скорее всего по умолчанию на любой запрос, если ресурс отсутствует физически на сервере, отдавалась index.html, которая запускала приложение. Далее, в соответствии с запросом из адресной строки, приложение отрисовывало необходимую страницу, либо страницу 404, если не находило соответствие. Теперь же, наш index.html уже не является пустым, а содержит контент главной страницы. Но страничка с пустой html-разметкой для случая попытки запуска страницы без html-копии всё же существует. Это та самая вышеупомянутая 200.html. Таким образом, на web-сервере необходимо перенастроить дефолтный ресурс для случая 404 с index.html на 200.html, чтобы избежать открытия “кривых” страниц (с контентом главной страницы поверх которого будет пытаться запуститься наш SPA) при обращении на страницы, html-копий для которых нет, либо просто при некорректном обращении на несуществующую страницу.

Meta-теги, заголовки, описания
Если с вышеописанным разобрались, то перейдем к реализации следующей части нашей задачи, а именно к необходимым meta-тегам и другим SEO-нюансам на страницах.
16099742302052703109502471075561.jpg
На заголовки <h1> и alt-ы для картинок особое внимание заострять не буду. Тут всё просто: идем по существующему js-коду react-компонентов страниц и добавляем там, где этого нет (а также не забываем это делать в дальнейшем для новых компонентов). А вот относительно meta-тегов title, description и keywords стоит немного поговорить отдельно. Они должны быть уникальными для каждой страницы. О том, зачем нужен каждый из них и как его стоит формировать, будет полезнее почитать более профильные материалы по SEO. Для нас же стоит более прагматичная задача – реализовать средствами js изменение контента данных тегов при навигации между страницами (таким образом у каждой html-копии страницы они будут разными как и положено, а при дальнейшей навигации по приложению после его запуска, они так же будут меняться в зависимости от текущей странички, но уже силами js приложения).

В целом, для реализации данного функционала есть готовый инструмент:
React-Helmet
Можно использовать его, либо на его основе написать что-то своё. Суть дела не изменится. Предлагаю просто перейти по ссылке и изучить то, что делает данный React-компонент и воспроизвести у себя необходимый функционал с его помощью либо написав собственное решение.
 
  • Wow
Реакции: Wolk