Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
В 2018 году я устроился на новую работу в стартап, связанный с высокорискованным кредитованием. На собеседовании мне говорили, что это мечта любого программиста. Необходимо переписать проект на современный стек.
На предыдущей работе, я создавал небольшие приложения для нескольких брендинговых агентств. В основном это были сайты разной сложности, но с полным отсутствием «легаси». В то время, я не знал такого слова.
Что же мне предстояло переносить — архитектурное чудо, написанное аутсорсерами на CakePHP в 2012 году. Интерфейс был построен на Twitter Bootstrap и jQuery. Из‑за усложнения бизнес логики, в проект добавили AngularJS и десяток библиотек для фингерпринтинга.
Я кивнул и взялся за рефакторинг. Сначала создал приложение на Angular. Затем поэтапно стал переписывать внешние вендоры с javascript на typescript. Были трудности, но они легко решались. Вопросы вызвал блог компании, который представлял собой набор мало связанных статей, ориентированный на поисковые системы.
Сначала я думал, что команда бэкенда предоставит API и я просто набросаю пару шаблонов и выведу содержимое. Однако, API делать никто не хотел, и посовещавшись, мы решили перенести статьи в wordpress.
Конечно, wordpress плохой выбор. Я был против, но не смог убедить коллег использовать что‑то другое, более подходящее.
Из CakePHP в Wordpress
Причина по которой отказались от нативного API — это административная панель для контента. У нас нет своих копирайтеров и процесс написания статей отдан на аутсорс. Предоставлять доступ к общей системе неизвестным людям мы не хотели, так как боялись за сохранность персональных данных.
Идея взять готовую CMS, которая предназначена для блоггинга, казалась команде лучшим решением. Система разворачивается отдельно и никак не связан с основным приложением. Даже если украдут логины и пароли от учетной записи, то данные клиентов не пострадают.
Из миллиона бесплатных систем для контента, мы выбрали знакомую многим копирайтерам CMS — Wordpress.
Основные преимущества Wordpress:
Одна из старейших CMS, которая популярна до сих пор. Большинство контент менеджеров ее знают и умеют с ней работать.
SEO из коробки. Wordpress ориентирован на продвижение в поисковых системах и соответственно предоставляет все необходимое для SEO.
Большое количество плагинов. Чтобы расширить функционал достаточно найти нужное решение и нажать кнопку установить.
Wordpress имеет API для созданного контента, что позволяет интегрировать с различными системами.
Субъективно, к явным плюсам я бы отнес — скорость разработки. На создание темы и миграцию статей я потратил около двух дней.
Недостатки использования Wordpress:
Развертывание приложения. CMS плохо совместима с системами контроля версий. Wordpress обновляется самостоятельно, меняя файловую структуру как ему вздумается. Если говорить про docker, то он не упрощает задачу. При инициализации образа, будет происходить начальный запуск скрипта. По крайней мере, так было в конце 2019 года.
Проблема работы приложения при изменении базового url. Например, wordpress расположить в /blog, то тогда навигация в админ панеле может быть сломана. Настройка префикса является не тривиальной задачей, при наличии балансировщика и кластера на kubernetes.
Стабильность работы API. Метод по запросу списка записей периодически падал с 500-й. Причину мы не нашли, но грешили на сетевые проблемы, связанные с кластером.
Еще один не очевидный минус — это человеческий фактор. Случалось пару раз, когда пользователи административной панели выключали необходимые плагины, и сайт ломался.
Конечно, потребовалось существенное количество времени, чтобы блог заработал. Мы успокаивали себя мыслью, что нужно немного потерпеть и забыть.
После релиза, какое‑то время была стабильность. Но потом, wordpress начал сыпаться. То плагины выключат, то балансировщик ломал префиксы в приложении, то API сыплет 500-тыми.
В итоге, когда речь заходила про wordpress, то разработчики начинали рыдать.
Блог я встраивал в текущий проект на Angular. Я сверстал похожую тему и при переходе на страницу с новостями, пользователь ничего не замечал. Единственной проблемой была авторизация. Когда клиент входит в учетную запись на нашем сайте, мы в шапке указываем имя и отчество. А так как блог никак не связан с основным ресурсом, то и пользователь был не авторизован.
Бизнес часто просил поправить синхронизировать сессии, но я все время откладывал эту задачу.
Что было дальше?
Статьями для нашего блога занималась компания на аутсорсе. Примерно семь лет, они следили за актуальными трендами и выкладывали релевантные материалы. С одного момента публикации прекратились и я не узнал причину. Наши маркетологи лишь отшучивались.
Меня немного расстраивал тот факт, что последняя новость была опубликована год назад. Поэтому я удалил ссылки на статьи из футера. А несколько месяцев спустя, наш девопс отключил сервис, который отвечал за Wordpress, чтобы в дальнейшем его удалить.
Но не прошло и месяца, как отдел маркетинга и рекламы нашел компанию, которая будет помогать нам с SEO. И первое, что они потребовали сделать — это блог.
Снова встал вопрос относительно публикации статей. Возвращаться к wordpress — выстрел себе в ногу. Я решил посмотреть в сторону статических генераторов сайтов (Static Site Generation, SSG), например, hugo или 11ty.
Hugo мне приглянулся. Я быстро собрал pet проект, подключил тему, вывел список статей. Hugo работал идеально, но был один нюанс:
Quod licet Iovi, non licet bovi.
Hugo берет файлы с расширением.md и генерирует HTML. И казалось, что сложного, создать документ и сделать разметку в markdown, а после закоммитить. Как показывает практика: Трудно научить копирайтера пользоваться git в windows.
Я занялся поиском альтернативного решения, которое позволило бы разработчику и контент менеджеру легко коммуницировать.
На одном из форумов, я увидел комментарий, связанный с SSG. Парень писал про связку 11ty и Contentful. 11ty альтернатива hugo, но contentful показался мне занятным и я решил попробовать его в своем проекте.
Вынос контента в Contentful
Contentful — это платформа для написания контента и доставки его на разные устройства.
Принцип работы с сервисом следующий:
Регистрируется новый workspace.
Создаются content‑type, которые будут использоваться в блоге.
Каждой сущности добавляется набор полей.
Как только готова структура, можно публиковать записи.
Весь контент доступен с помощью API. На выбор RESTи GraphQL. Для того чтобы использовать API, необходимо создать ключ и отправлять его с запросом.
Отмечу, что в бесплатной версии есть некоторые ограничения:
размер хранилища ~ 0.7 TB. Файл не должен превышать 50 Mb;
25 content type;
10 000 записей.
Лимитов более чем достаточно для публикации новостей. Я быстро накидал структуру:
Post;
Author;
Category;
Image (Asset).
Contentful сопровождается хорошей документацией и я за пару протестировал API. Меня интересовали три запроса:
выборка всех сущностей;
получение фильтрованного списка постов по выбранной категории;
загрузка публикации по ID.
Все отлично отдается. Осталось только подружить Contentful c Angular.
Интеграция Contentful и Angular
Contentful для Angular представляет собой обычное API, которое ничем не отличается от другого API. Задача Angular в этом случае, получить данные от API и отобразить их.
Типичный блог состоит из трех частей:
Лента на главной странице.
Статьи в конкретной категории.
Публикация целиком.
Так как первый и второй пункт различаются только содержанием, то достаточно создать компонент списка записей и еще один для вывода полной статьи.
Заполнение в классической модели выглядит следующим образом: загрузить требуемые данные по API и вывести их.
В такой реализации теряется суть SSG. Один из плюсов SSG — это скорость. Отображение готового HTML всегда будет быстрее и эффективнее, чем запрос данных с сервера.
В Angular для Server Sider Rendering используют Universal. Пререндер из universal позволяет по карте маршрутов сгенерировать статичные страницы. Это необходимо для SEO, так как по умолчанию Angular отдает пустой html.
Если пререндеру передать /post-1
, /post-2,
то он создаст следующие файлы:
dist/app/browser/post-1/index.html
dist/app/browser/post-2/index.html
Если пользователь перейдет адресам /post-1 или /post-2, то сервер в качестве ответа, отдаст ранее созданные страницы. Однако, после загрузки Angular начнет восстанавливать состояние, а также выполнять API запросы.
Переходя по внутренним роутам, пользователь не увидит других страниц prerender‑а. Angular будет создавать их на лету. Клиент получит отрисованную версию если принудительно сделает рефреш.
Angular можно добавить преимущества SSG с помощью лайфхака. Суть метода заключается в том, чтобы вместо API использовать мета данные из роута.
При создании навигации в Angular есть возможность передавать любые значения в компонент. Это делается с помощью свойства data.
Пример:
import { Route } from '@angular/router';
export const blogRoutes: Route[] = [
{
path: '',
loadComponent: () => import('@angular-blog/posts/view/page').then((modules) => modules.PostViewPageComponent),
data: {
post: {
tags: [],
published: '2023-08-05T00:00+07:00',
title: 'Ядерное оружие, строительство метро и почитание миллионов: к годовщине гибели последнего русского императора и его семьи',
description: 'Николай II был не только талантливым государственным деятелем и военачальником, но и выдающимся творческим человеком',
category: { slug: 'stati', name: 'Статьи' },
image: '//images.ctfassets.net/5lmjt5hjp7mm/1eoB41kQiSaQu9Uh4FlU3E/b38718f091199923f82e4acf2b7965ab/p18.webp',
imageOriginal: {
fields: {
title:
'Ядерное оружие, строительство метро и почитание миллионов: к годовщине гибели последнего русского императора и его семьи',
description: '',
file: {
url: '//images.ctfassets.net/5lmjt5hjp7mm/1eoB41kQiSaQu9Uh4FlU3E/b38718f091199923f82e4acf2b7965ab/p18.webp',
details: { size: 46762, image: { width: 800, height: 450 } },
fileName: 'p18.webp',
contentType: 'image/webp',
},
},
},
author: {
fullName: 'Стасик Птичкин',
email: 'stas@fafn.ru',
avatar: '//images.ctfassets.net/5lmjt5hjp7mm/2JQGPr04yvSVmpuH6JIZYA/656c3939dcbd0296dc9c32b99e86cdb4/s3.jpg',
},
headline:
'Ядерное оружие, строительство метро и почитание миллионов: к годовщине гибели последнего русского императора и его семьи',
intro:
'Сегодня весь мир скорбит о Николае II, которого вместе с семьёй 105 лет назад расстреляли иуды-большевики. Вот уже больше века русофобы и коммунисты пытаются оклеветать последнего императора, но «Панорама» даже в самые тёмные годы сталинской диктатуры продолжала писать правду о Николае Александровиче. ',
slug: 'yadernoe-oruzhie-stroitelstvo-metro-i-pochitanie-millionov-k-godovshine',
body: "<p>Сегодня весь мир скорбит о Николае II, которого вместе с семьёй 105 лет назад расстреляли иуды-большевики. Вот уже больше века русофобы и коммунисты пытаются оклеветать последнего императора, но «Панорама» даже в самые тёмные годы сталинской диктатуры продолжала писать правду о Николае Александровиче.</p><p>Оно и понятно – если бы не незаконный большевистский переворот, то ядерное оружие появилось бы у Российской империи ещё в годы Первой мировой войны.</p><p>Николай II был не только талантливым государственным деятелем и военачальником, но и выдающимся творческим человеком. Именно он создал проект высоток, которые впоследствии у него украл бессовестный Сталин. Большевики пытались скрыть, что именно при императоре в Петербурге появилось секретное метро, также незаслуженно забыт царский космолёт. Заслуги последнего царя в освоении межзвёздного пространства признали на самом высоком уровне – в честь Николая II назвали российскую космическую станцию.</p><p>К сожалению, часть имущества нашего святого императора осталось за пределами России. Достаточно вспомнить о царском мобильном телефоне, который был похищен агентами британской разведки в 1917 году и тайно вывезен в Лондон.</p><p>Но память народа жива, люди чтят своего царя. Достаточно вспомнить, с каким восторгом во всех странах СНГ встречали мощи Николая II. В своей бессильной злобе наследники большевиков-христопродавцев пытаются всячески поглумиться над памятью императора. Миллионы граждан были возмущены, когда банда коммунистов украла у Натальи Поклонской коллекцию икон святого мученика. А инициатива назначить Николая II пожизненным сенатором вызвала небывалое единение в нашем обществе.</p><p>Завершить мы бы хотели призывом установить памятник нашему любимому императору в каждом райцентре. Для экономии можно переделать монументы Ленину – опыт модернизации скульптур у отечественных специалистов есть. <a routerLink='/'>Домой</a></p>",
},
sitemap: {
loc: '/yadernoe-oruzhie-stroitelstvo-metro-i-pochitanie-millionov-k-godovshine',
},
meta: {
title: 'Ядерное оружие, строительство метро и почитание миллионов: к годовщине гибели последнего русского императора и его семьи',
description: 'Николай II был не только талантливым государственным деятелем и военачальником, но и выдающимся творческим человеком',
image: '//images.ctfassets.net/5lmjt5hjp7mm/1eoB41kQiSaQu9Uh4FlU3E/b38718f091199923f82e4acf2b7965ab/p18.webp',
imageType: 'image/webp',
imageWidth: '800',
imageHeight: '450',
},
breadcrumbs: [
{
label: 'Блог',
route: '/',
},
{
label: 'Статьи',
route: '/category/stati',
},
],
},
},
];
В таком случае, можно написать небольшой скрипт, который будет загружать все статьи из Contentful и генерировать страницы с мета данными.
Как я раньше упоминал, для блога нужно всего два типа компонента. Единственной проблема — это количество публикаций. Необходимо разбить все статьи на группы по десять штук и сделать пагинацию.
Для блога с двумя категориями по двадцать материалов в каждой, получается 46 роутов.
Главная и лента новостей, 2 шт:
/
/feed/2
Список статей в конкретном разделе, 4 шт:
/category/first
/category/first/2
/category/second
/category/second/2
Страница публикации — 40 шт.
Демонстрация
Если проделать вышеописанные манипуляции, то получится что‑то наподобие этого:
Демо можно глянуть тут — angular‑blog.fafn.ru.
Исходный код на github — https://github.com/Fafnur/angular‑blog.
В следующей статье — Создание статичного блога на Angular и Contentful я расскажу подробно про реализацию блога на Angular и Contentful.