Главная » Статьи » SEO в Angular без рендеринга на стороне сервера

SEO в Angular без рендеринга на стороне сервера

SEO в Angular без рендеринга на стороне сервера

От автора: знаете ли вы, что Google Search поддерживает сканирование JavaScript с мая 2019 года? Это означает, что рендеринг на стороне сервера больше не единственный способ улучшить SEO.

Введение: Angular и SEO

Классическая проблема одностраничных приложений и поисковых систем

Одностраничные приложения (SPA) имеют только одну главную страницу, index.html, которая получает обновления DOM с помощью JavaScript.

До недавнего времени у поисковых систем были проблемы с запуском таких приложений. У всех современных JavaScript-фреймворков был серьезный недостаток с точки зрения SEO.

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

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

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

Текущее состояние поиска Google

Google Dev Summit 2020 дал разработчикам много полезных советов. Я нашел вдохновение для этой статьи в этом видео, где адвокат разработчиков Google Мартин Сплитт проливает свет на эту тему. В итоге:

Может ли Google обнаруживать изменения в приложениях Javascript?

Разработчикам основных javascript-фреймворков, таких как Angular, React или Vue, не стоит беспокоиться. С мая 2019 года Google Search поддерживает Javascript.

Полезны ли заголовок и мета-описание для SEO?

… Они могут косвенно повлиять на SEO через показатель кликабельности

Руководство по внедрению

Встроенные инструменты Angular: DOCUMENT, Meta & Title

Мы можем получить доступ к DOCUMENT для редактирования его Title и Meta с помощью инструментов, предоставленных в @angular/common и @angular/platform-browser.

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

import { DOCUMENT } from '@angular/common';
import { Inject, Injectable } from '@angular/core';
import { Meta, Title } from '@angular/platform-browser'; @Injectable({ providedIn: 'root'
})
export class SEOService { constructor( @Inject(DOCUMENT) private dom, private titleSvc: Title, private metaSvc: Meta, ) { } updateTitle(title: string){ this.titleSvc.setTitle(title) } updateDescription(content: string) { this.metaSvc.updateTag({ name: 'description', content }) } createCanonicalLink(url: string) { let link: HTMLLinkElement = this.dom.createElement('link'); link.setAttribute('rel', 'canonical'); link.setAttribute('href', url); this.dom.head.appendChild(link); } }

Пример использования: статические метаданные

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

Поскольку эти свойства связаны с определенным маршрутом, мы будем использовать Router для отправки информации компоненту. Angular Routes позволяет отправлять любые статические данные, используя данные свойства.

Добавление метаданных к существующему маршруту

SEO в Angular без рендеринга на стороне сервера

Чтобы все было понятно, создайте один файл для хранения всех статических метаданных вашего сайта

После определения некоторого мета статического раздела мы добавляем его в маршрутизатор.

const sectionRoutes: Routes = [ { path: '', data: sectionsMetadata.homePage, children: [ { path: 'articles', data: sectionsMetadata.articles, loadChildren: () => import('./pages/articles/articles.module').then(m => m.ArticlesModule), }, ] },
] @NgModule({ declarations: [], imports: [ CommonModule, RouterModule.forRoot(sectionRoutes), ], exports: [RouterModule]
})
export class AppRoutingModule { }

Затем мы можем обновить мета, когда компоненты инициализируются:

@Component({ selector: 'app-articles', templateUrl: './articles.component.html', styleUrls: ['./articles.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ArticlesComponent implements OnInit { readonly articles$ = this.route.data.pipe(pluck('articles')) constructor( private SEOService: SEOService, private route: ActivatedRoute, ) { } ngOnInit(): void { const { meta } = this.route.snapshot.data; this.SEOService.updateTitle(meta.title); this.SEOService.updateDescription(meta.description); } }

Пример использования: метаданные динамического содержимого

Но… что происходит, когда метаданные поступают из элемента внутри HttpResponse? Когда дело доходит до динамического контента, следует учитывать две вещи: навигацию по параметрам и асинхронные данные.

Динамический контент в навигации по параметрам

Когда мы переходим от одной статьи к другой, используя маршрут с такими параметрами, как articles/:id, компонент не создается повторно. Он просто обновляет и повторно отображает контент.

Это означает, что нам нужно обрабатывать изменения через Observables, если мы хотим динамически обновлять метаданные.

Использование резолвера для предварительной выборки асинхронных данных

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

Это можно сделать с помощью Angular resolver. Мы делаем HTTP-запрос и ждем ответа, прежде чем запускать навигацию.

@Injectable({ providedIn: 'root' })
export class ArticleResolver implements Resolve<Article> { constructor(private articlesService: ArticlesAPIService) {} resolve(route: ActivatedRouteSnapshot): Observable<Article> { const id = route.paramMap.get('id'); return this.articlesService.getArticle$(id); }
}

Добавление резольвера в маршрутизатор

import { Routes, RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common'; import { ArticleResolver } from './_providers/article.resolver';
import { ArticlesComponent } from './articles/articles.component'; export const articlesRoutes: Routes = [ { path: '', component: ArticlesComponent, }, { path: ':id', resolve: { article: ArticleResolver }, loadChildren: () => import('./article/article.module').then(m => m.ArticleModule) } ] @NgModule({ declarations: [ArticlesComponent], imports: [ CommonModule, RouterModule.forChild(articlesRoutes), ]
})
export class ArticlesModule { }

Обновление компонента

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

@Component({ selector: 'app-article', templateUrl: './article.component.html', styleUrls: ['./article.component.css'], changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ArticleComponent { readonly article$: Observable<Article> = this.route.data .pipe( pluck('article'), tap(article => this.updatePageMeta(article)) ); constructor( private SEOSvc: SEOService, private route: ActivatedRoute ) { } private updatePageMeta(article: Article) { this.SEOSvc.updateTitle(article.title); this.SEOSvc.updateDescription(article.description) } }

Как всегда, спасибо за внимание.

Автор: Fer Ayguavives

Источник: medium.com

Редакция: Команда webformyself.