Сайт визитка на Angular. Настройка SEO.
В данной статье настроим SEO (Search Engine Optimization), добавив необходимую конфигурацию, а также указав мета теги и другую вспомогательную информацию.
Angular в базовом состоянии скудно сконфигурирован. В базовое приложение необходимо добавить в приложение:
favicon
— иконка для закладок, которая в зависимости от типа устройства нормально масштабировалась;webmanifest
(browserconfig
)— конфигурация сайта дляPWA
, которая содержит названия и цвета устанавливаемого приложения;robots.txt
— политика индексирования в поисковых системах;sitemap.xml
— карта сайта;site-image.jpg
— картинка, которая будет использоваться при шаринге.
Для того, чтобы поисковые системы различали страницы, необходимо для каждой страницы приложения задать свои мета теги.
Список страниц, на которые необходимо добавить теги:
/
— главная страница/product/*
— страница товара/support
— страница службы поддержки/terms
— страница условий продажи товаров/cart
— корзина/order
— страница оформления заказа.
Favicons
Обычно в качестве favicon
выступает логотип. Используя логотип, а также любой сервис для генерации favicon
, создадим набор иконок:
apps/store/src/assets/images/favicons
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon.png
├── favicon-16x16.png
├── favicon-32x32.png
├── mstile-150x150.png
└── safari-pinned-tab.svg
В index.html
добавим в head
:
link rel="apple-touch-icon" sizes="180x180" href="/assets/images/favicons/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/assets/images/favicons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/assets/images/favicons/favicon-16x16.png" />
<link rel="mask-icon" href="/assets/images/favicons/safari-pinned-tab.svg" color="#d01931" />
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="theme-color" content="#ffffff" />
Webmanifest
Сервисы генерации favicons
обычно вместе с файлами иконок, генерируют и файлы манифеста.
Создадим site.webmanifest
и browserconfig.xml:
Также подключим в index.html
:
<link rel="manifest" href="/site.webmanifest" />
Robots.txt
Определим политику индексирования сайта:
User-agent: *
Disallow: /api/
User-agent: Yandex
Disallow: /api/
Clean-param: bonus&utm_source&utm_medium&utm_campaign&utm_term&utm_content&click_id
Host: https://banshop.fafn.ru
Sitemap: https://banshop.fafn.ru/sitemap.xml
В данном случае:
- запрещаем индексировать все страницы с префиксом
/api/
; - для
Yandex
еще явно указываем сбор параметров; - указываем хост и карту сайта.
Sitemap
Создания карты сайта является одной из проблем Angular, которую никто не хочет решать.
Используя рекомендации Angular по созданию отдельного модуля для навигации, в качестве даты (“data”) укажем параметры для карты сайта:
В файле libs/cart/page/src/lib/cart-page-routing.module.ts
:
Для данного роута была добавлена следующая конфигурация:
sitemap: {
loc: '/cart',
priority: '1.0',
},
В итоге в карте сайта должен появится следующий url
:
<url>
<loc>https://banshop.fafn.ru/cart</loc>
<lastmod>2022-03-19</lastmod>
<changefreq>daily</changefreq>
<priority>1.0</priority>
</url>
Для того чтобы это произошло, реализуем генератор карты сайта.
Создадим новый файл apps/store/sitemap-generator.ts
:
Генератор содержит несколько функций
fromDir
— читает содержимое директории и ищет файлы по шаблону;parseSitemapConfig
— парсит содержимое файла и пытается найти конфигурациюsitemap
;getSitemapUrl
— генерация пути в sitemap на основании входящего конфига;getServerData
— загрузка внешнего файла и генерация url’ов на основе полученных данных;getUrls
— функция, которая парсит приложение и библиотеки для конкретного приложения на наличие файлов роутинга;generate
— функция, которая запускает генерацию карты сайта.
Так как ts-node нужен tsconfig
, создадим конфиг на основе базового tsconfig.base.json
, добавив в проект файл tsconfig.sitemap.json
:
Из важного, в конфигурации указывается тип модуля — CommonJS
, который позволит ts-node
корректно обрабатывать импорты и другие сущности.
Для запуска будем использовать следующую команду, которую добавим в package.json
:
"generate-sitemap": "ts-node --project=apps/store/tsconfig.sitemap.json apps/store/sitemap-generator.ts"
Для вызова запустим команду:
yarn generate-sitemap
В результате выполнения скрипта, получим следующую карту сайта:
Из важного, стоит отметить, что вместе с генерацией карты сайта, генерируется массив страниц для prerender’а:
const routePaths = [...Array.from(routes), '/not-found', '/server-error'].sort().join('\n');
fs.writeFileSync('apps/store/routes.txt', routePaths);
Это позволяет в фазе prerender’а отрисовать все требуемые страницы в приложении. Подробнее об этом будет в следующей статье.
Meta tags
Для мета тегов используется почти аналогичный подход.
Мета теги указываются в приложении в момент окончания навигации, где смотрится дата для текущего роута и на страницу вставляются нужные данные.
Реализуется это следующим образом:
Выше описанные интерфейсы позволяют описать конфигурацию мета тегов
MetaConfig
— базовые мета теги, такие какkeys
,description
иtitle
MetaConfigOg
— мета теги схемы OG.
Также в файле предоставлены мета теги по умолчанию, включая локализацию:
export const META_CONFIG_DEFAULT: MetaConfig = {
title: $localize`:Meta default|:Online store Banshop`,
// eslint-disable-next-line max-len
description: $localize`:Meta default|:Banshop sportswear, shoes and accessories. Free delivery in Moscow and all over Russia when paying on the website.`,
keywords: $localize`:Meta default|:sneakers, sports shoes`,
};
export const META_CONFIG_OG_DEFAULT: MetaConfigOg = {
title: META_CONFIG_DEFAULT.title,
description: META_CONFIG_DEFAULT.description,
type: 'website',
image: '/assets/images/site.jpg',
imageType: 'image/jpeg',
imageWidth: '600',
imageHeight: '284',
};
Используя прошлый пример, рассмотрим дату для пути:
const routes: Routes = [
{
path: '',
component: CartPageComponent,
data: {
sitemap: {
loc: '/cart',
priority: '1.0',
},
meta: {
title: $localize`:Cart meta|:Cart | Online store Banshop`,
description: $localize`:Cart meta|:It is very easy to buy on banshop. To place an order, click the order button.`,
keywords: $localize`:Cart meta|:cart, banshop`,
},
} as Partial<RouteData>,
},
];
Как видно, в data
есть специальный раздел, который описывает конфигурацию мета тегов для текущей страницы:
meta: {
title: $localize`:Cart meta|:Cart | Online store Banshop`,
description: $localize`:Cart meta|:It is very easy to buy on banshop. To place an order, click the order button.`,
keywords: $localize`:Cart meta|:cart, banshop`,
},
И так как это общий файл, для него создаются собственные переводы при необходимости.
Сервис задания мета тегов достаточно прост:
Сервис получает доступ к заголовку страницы, роутеру, параметрам приложения, а также набору токенов, которые предоставляют собой дефолтные параметры для мета тегов (META_CONFIG
, META_CONFIG_OG
).
В MetaService
реализованы следующие методы:
setCanonicalUrl
— установление canonical url, который считается основным для множества страницgetCanonicalURL
— получение canonical urlsetMetaProperty
— метод, который по ключу создает или обновляет мета тегupdate
— метод, который вызывает обновления коллекции свойств на странице.
update(metaConfig?: Partial<MetaConfig>, metaConfigOg?: Partial<MetaConfigOg>): void {
const config: MetaConfig = { ...this.metaConfig, ...metaConfig };
const configOg: MetaConfigOg = { ...this.metaConfigOg, ...metaConfigOg };
this.setCanonicalUrl(config.url);
this.titleService.setTitle(`${config.title} | ${this.environmentService.environments.brand}`);
this.setMetaProperty('description', config.description);
this.setMetaProperty('keywords', config.keywords);
this.setMetaProperty('og:title', `${configOg.title ?? config.title} | ${this.environmentService.environments.brand}`);
this.setMetaProperty('og:description', configOg.description ?? config.description);
this.setMetaProperty('og:type', configOg.type);
this.setMetaProperty('og:locale', configOg.locale ?? this.localeId);
this.setMetaProperty('og:site_name', configOg.siteName ?? this.environmentService.environments.brand);
this.setMetaProperty('og:image', `${this.environmentService.environments.appHost}${configOg.image}`);
this.setMetaProperty('og:image:type', configOg.imageType);
this.setMetaProperty('og:image:width', configOg.imageWidth);
this.setMetaProperty('og:image:height', configOg.imageHeight);
}
Если посмотреть на метод обновления свойств, то там можно увидеть, что сначала формируются наборы мета тегов, а затем используя знания о доступных мета тегах и идет обновление каждого доступного тега.
Для того чтобы заставить Angular динамически обновлять теги, создадим модуль и NgrxEffect
, который будет отслеживать окончание навигации и вызывать метод обновления мета тегов.
Так store
знает об окончании навигации запуская соответствующий экшен — ROUTER_NAVIGATED
, то подписавшись на него, можно вызывать метод обновления:
routerNavigated$ = createEffect(() => {
return this.actions$.pipe(
ofType(ROUTER_NAVIGATED),
fetch({
run: (action: NavigationActon) => {
const {meta,metaOg}= action.payload.routerState?.data ?? {};
this.metaService.update(
{
url: action.payload.routerState.url,
...meta,
},
metaOg
);
},
})
);
});
Подключение модуля MetaStateModule
в AppCoreModule
позволит динамически менять мета теги у страниц.
Ссылки
Предыдущая статья — Настройка SEO.
Следующая статья — Настройка SSR.
Все исходники находятся на github, в репозитории:
Для того, чтобы посмотреть состояние проекта на момент написания статьи, нужно выбрать соответствующий тег — article.
Подписывайтесь на блог, чтобы не пропустить новые статьи про Angular, и веб-разработку. Medium | Telegram| VK |Tw| Ln