Создание блога на Angular с помощью Scully и развертывание на Netlify

Мы создадим сайт блога с помощью фреймворка Angular, Scully для генерации статических сайтов (SSG), а затем развернем проект на Netlify, чтобы он был доступен для публики.

О чем пойдет речь в этой статье

  • Использование Angular для создания архитектуры блога
  • Установка библиотеки Scully для работы со статической генерацией сайта
  • Развертывание на Netlify
  • Демонстрация этого проекта
  • Репозиторий для демонстрации

Требования к настройке

Мы будем использовать следующие инструменты для этого проекта

  • Angular v13
  • Tailwind
  • Scully
  • Node v14.15 (или выше)

Angular — это фреймворк и платформа разработки приложений для создания эффективных и сложных одностраничных приложений. Это фреймворк с открытым исходным кодом, созданный компанией Google.

Scully — это библиотека, используемая для генерации статических сайтов, построенных с помощью Angular, и вводящая вас в джамстек. SSG генерирует статические страницы, когда вы создаете свой проект, так что то, что вы публикуете/развертываете — это уже созданные страницы, которые могут быть проиндексированы google (для некоторых из этих SEO хороших вещей) и быстро предоставлены клиентам. Без scully наш ангулярный проект будет проходить через рендеринг на стороне клиента, где каждая посещаемая страница приложения рендерится браузером во время выполнения.

Tailwind — это библиотека утилит CSS, которая используется для ускорения стилизации приложений за счет гибкости и лаконичности.

Теперь, когда у нас есть некоторые сведения о проекте, мы можем приступить к созданию

Генерация проекта Angular

Используйте приведенную ниже команду для генерации нового проекта Angular. Вы можете назвать свой проект так, как вам нравится. Я выбрал angular-blog.

ng new angular-blog
Войдите в полноэкранный режим Выйти из полноэкранного режима

Вы увидите следующие вопросы. Нам нужна маршрутизация и формат таблицы стилей SCSS.

? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS 
Войти в полноэкранный режим Выйти из полноэкранного режима

После того как проект будет сгенерирован, обслужите приложение с помощью команды ng serve

ng serve
Войти в полноэкранный режим Выйти из полноэкранного режима

Вас встретит стандартный шаблон приветствия angular.

Перейдите в app.component.html и удалите все содержимое (мы не хотим, чтобы эта страница приветствия отображалась), а затем поместите теги <router-outlet></router-outlet>, поскольку именно здесь будут отображаться все наши модули и компоненты на основе конфигурации маршрута в app.routing-module.ts.

Установка Scully

Выполните приведенную ниже команду для установки scully и автоматического внесения всех необходимых конфигураций в проект

ng add @scullyio/init
Войти в полноэкранный режим Выйти из полноэкранного режима

Во время установки вы получите вопрос, приведенный ниже. На момент написания этой статьи pupeteer является наиболее стабильным рендерером, поэтому мы выберем его.

? Which route renderer would you like to use?
  Scully platform server
> Puppeteer
  Playwright (beta)
Войти в полноэкранный режим Выйти из полноэкранного режима

Вот как выглядит терминал после процесса установки

В схеме init установки scully была сделана пара изменений, которые включают в себя;

  • установка плагина pupeteer
  • обновление app.module.ts путем импорта ScullyLibModule
  • обновление файла package.json с командами scully для создания и обслуживания сгенерированных страниц.
    "scully": "npx scully --",
    "scully:serve": "npx scully serve --"
Вход в полноэкранный режим Выйти из полноэкранного режима
  • создание файла scully.angular-blog.config.ts, который является конфигурационным файлом scully.

Добавление поддержки блогов в наш проект

В Scully есть схема создания раздела блога для нашего проекта

ng generate @scullyio/init:blog
Вход в полноэкранный режим Выйти из полноэкранного режима

Это добавляет лениво загруженные маршруты модуля блога в приложение Angular (app.routing-module.ts) и создает папку ./blog для файлов разметки блога (где будут храниться наши записи блога).
Ниже приведена текущая конфигурация маршрутов в app.routing-module.ts, обновленная scully.

const routes: Routes = [{ path: 'blog', loadChildren: () => import('./blog/blog.module').then(m => m.BlogModule) }];
Войти в полноэкранный режим Выйти из полноэкранного режима

Он также обновляет scully.angular-blog.config.ts с маршрутами блога для пререндера.

Несколько других конфигураций

Теперь у нас есть модуль для нашей функции блога. Следующие вещи, которые нам нужно создать, это;

  • компонент для списка записей блога с помощью команды ниже
ng generate component blog/blog-list
Войти в полноэкранный режим Выход из полноэкранного режима
  • компонент, который будет показывать фактическое содержимое записи блога, используя команду ниже
ng generate component blog/blog-post
Войти в полноэкранный режим Выйти из полноэкранного режима

Нам также нужно перейти к blog.routing.module.ts и исправить нашу конфигурацию маршрута, как показано ниже

const routes: Routes = [
  {
    path: '',
    component: BlogComponent,
    children: [
      {path: ':slug', component: BlogPostComponent},
      {path: '', component: BlogListComponent}
      ]
  }
];
Войти в полноэкранный режим Выйти из полноэкранного режима

Наконец, мы добавляем <router-outlet></router-outlet> в blog.component.html для обработки маршрутизации в модуле. Эта страница будет отображать список блогов и посты в блоге на основе конфигурации маршрута в blog-routing.module.ts.

Настройка точки входа маршрута для scully

После выбора модуля для нашего блога нам нужно создать точку входа для scully (обычно это главная страница нашего приложения).

Для домашней страницы нашего приложения целесообразно использовать функцию Angular lazy-loaded module, поскольку эта часть нашего приложения отделена от части блога.
Создайте модуль home с помощью следующей команды:

ng generate module home --route=home --module=app-routing
Войти в полноэкранный режим Выйти из полноэкранного режима

Это обновит конфигурацию маршрута в app-routing.module.ts с путем для модуля home. Установите путь к нему в пустую строку.

const routes: Routes = [{
        path: 'blog',
        loadChildren: () => import('./blog/blog.module').then(m => m.BlogModule)
    },
    {
        path: '',
        loadChildren: () => import('./home/home.module').then(m => m.HomeModule)
    },
    {path: '**', redirectTo: 'blog', pathMatch: 'full'},
];
Вход в полноэкранный режим Выход из полноэкранного режима

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

{path:'',redirectTo:'blog',pathMatch:'full'},
Вход в полноэкранный режим Выход из полноэкранного режима

Создание приложения Angular-Scully

После всех этих настроек нам нужно собрать наш проект с помощью Angular, собрать с помощью Scully, затем обслужить собранный проект на сервере Scully, но сначала давайте перезапустим сервер Angular.

Соберите проект, выполнив следующие действия

ng build
Войдите в полноэкранный режим Выйти из полноэкранного режима

Затем мы собираем проект с помощью Scully, используя

npx scully
Войти в полноэкранный режим Выйти из полноэкранного режима

Мы также можем изменить команду build в нашем package.json так, чтобы она выполняла две предыдущие команды.

"build": ng build && npm run scully -- --scanRoutes
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь мы можем использовать npm run build для достижения того же самого.
После выполнения этих двух команд в корневом каталоге нашего проекта будет создана папка dist. Эта папка содержит две собранные версии нашего сайта. Папка с именем angular-blog — это версия, созданная Angular, а папка с именем static содержит статические страницы, созданные scully.
Теперь мы можем запустить сервер scully с помощью команды:

npx scully serve
Войти в полноэкранный режим Выйти из полноэкранного режима

После успешного запуска сервера перейдите по адресу http://localhost:4200/blog, чтобы увидеть работающее приложение angular, затем в отдельной вкладке откройте эту ссылку http://localhost:1668/blog, чтобы увидеть статический сайт. Ваш браузер должен показать белую страницу с текстом blog-list works!.

Чтобы проверить наш фактический пост в блоге (созданный схемой init:blog), перейдите к файлу markdown в папке blog и скопируйте значение свойства slugs в frontmatter.

---
title: "2022-04-29-blog"
description: 'blog description'
published: false
slugs:
    - ___UNPUBLISHED___l2kp8pyl_z4RfUZ7hSOMdOfldi8yqeZcOKuebIId2
---
Вход в полноэкранный режим Выйти из полноэкранного режима

Это текущий путь (сгенерированный scully) к записи блога, когда published: false. Это приватный путь к записи вашего блога, когда она еще находится в черновой версии, чтобы вы могли поделиться с кем угодно, чтобы получить рецензию на статью перед публикацией. Когда вы будете готовы опубликовать пост, вы можете установить published: true и удалить свойство slugs, что позволит scully использовать имя файла (без расширения) в качестве пути при генерации статических страниц.

Перейдите в адресную строку вкладки, обслуживающей статический сайт, и вставьте slug после http://localhost:1668/blog/, затем нажмите Enter. Вы должны увидеть содержимое записи блога.

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

Создание записей блога

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

ng generate @scullyio/init:post --name="Post 1"
Войти в полноэкранный режим Выйти из полноэкранного режима

Это создаст файл уценки в папке blog.
Когда мы открываем только что созданный файл уценки, мы видим следующее

---
title: Post 1
description: blog description
published: false
---

# Post 1

Войти в полноэкранный режим Выход из полноэкранного режима

Как вы теперь знаете, содержимое между тремя тире — это frontmatter, а под frontmatter находится содержимое блога.

Frontmatter — это место, где мы можем добавить информацию о посте, такую как метаданные, SEO и т.д.
Давайте обновим этот файл разметки, чтобы он выглядел следующим образом

---
title: Post 1
description: blog description
published: true
image: /assets/images/enigma.jpg
seo: {
metaDescription: First post for my angular scully blog,
metaTitle: First post
},
---

# Post 1

Войти в полноэкранный режим Выйти из полноэкранного режима

Я добавил изображение в соответствующую директорию, которое будет отображаться в посте.
Когда мы удовлетворены нашим файлом разметки, мы можем собрать его с помощью команды npx scully.

Совет по выбору команды для сборки

Команда ng build нужна только в том случае, если вы что-то изменили в вашем angular-приложении. Конфигурационный файл scully, плагины и возможные файлы разметки не являются частью вашего приложения Angular. Хотя это может показаться очевидным, если вы впервые используете Scully, легко перестроить Angular, даже если в этом нет необходимости. При написании плагинов Scully ИЛИ изменении файлов разметки вашего блога, вам НЕ нужно ng build приложение каждый раз при повторном запуске Scully. Опять же, ng build Angular нужно делать только в том случае, если приложение Angular изменяется.

Показ всех записей блога

Давайте перейдем к blog-list.component.ts, чтобы разработать логику обработки данных для компонента.
В этот компонент мы внедрим ScullyRoutesService в качестве зависимости, которая даст нам доступ к маршрутам записей блога.

import { Component, OnInit } from '@angular/core';
import {ScullyRoute, ScullyRoutesService} from "@scullyio/ng-lib";
import {Observable} from "rxjs";

@Component({
  selector: 'app-blog-list',
  templateUrl: './blog-list.component.html',
  styleUrls: ['./blog-list.component.scss']
})
export class BlogListComponent implements OnInit {
  links$: Observable<ScullyRoute[]> = this.scully.available$;

  constructor(private scully: ScullyRoutesService) { }

  ngOnInit(): void {
    this.getPublishedPosts();
  }

  getPublishedPosts() {
    this.links$ = this.links$.pipe(tap(val) => console.log(val))
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Мы хотим отобразить опубликованные посты, поэтому мы получаем опубликованные маршруты из свойства available$ на ScullyRoutesService.

В blog-list.component.html мы можем построить шаблон, который показывает все записи блога. Пока что мы будем работать с кодом, приведенным ниже.

<div>
  <ul>
    <li *ngFor="let post of links$ | async">
    <a [href]="post.route">{{post.title}}</a>
    </li>
  </ul>
</div>
Вход в полноэкранный режим Выйдите из полноэкранного режима

После сохранения вы должны увидеть в консоли массив из двух маршрутов. Мы пока не видим ни одного маршрута с записями из блога, потому что мы не собрали scully после изменения свойства published на true.

Давайте убедимся, что свойство published в наших файлах разметки установлено в true, затем соберем с помощью scully, чтобы обновить наши опубликованные маршруты.
Теперь при перезагрузке мы должны увидеть в консоли массив из 4 маршрутов. Однако, поскольку нам нужны маршруты только для записей блога, нам нужно преобразовать наши данные с помощью rxjs.
Давайте обновим функцию getPublishedPosts.

getPublishedPosts() {
    this.links$ = this.links$.pipe(map((links) => links.filter((link) =>
      link.route.startsWith('/blog/'))),tap((val) => console.log(val)));
  }
Вход в полноэкранный режим Выйти из полноэкранного режима

Чтобы убедиться, что версия нашего сайта для scully обновлена, выполним команду:

npm run build
Enter fullscreen mode Выйти из полноэкранного режима

Теперь наша страница должна выглядеть следующим образом.

Вы можете щелкнуть по ссылкам, чтобы увидеть, что они показывают соответствующие посты.

Доступ к данным на странице записи блога

Мы можем получить данные маршрута конкретной страницы, на которую мы переходим, с помощью ScullyRoutesService, подписавшись на наблюдаемую, возвращаемую методом getCurrent().
Добавьте приведенный ниже код в blog-post.component.ts.

import {Component, OnDestroy, OnInit} from '@angular/core';
import {ScullyRoute, ScullyRoutesService} from "@scullyio/ng-lib";
import {Subject, takeUntil} from "rxjs";
import {tap} from "rxjs/operators";

@Component({
  selector: 'app-blog-post',
  templateUrl: './blog-post.component.html',
  styleUrls: ['./blog-post.component.scss']
})
export class BlogPostComponent implements OnInit, OnDestroy {
  currentRoute: ScullyRoute = {} as ScullyRoute;
  onDestroy$ = new Subject<void>();

  constructor(private scully: ScullyRoutesService) {
  }

  ngOnInit(): void {
    this.getCurrentPost()
  }

  getCurrentPost() {
    this.scully.getCurrent()
      .pipe(takeUntil(this.onDestroy$))
      .subscribe((routeData: ScullyRoute) => {
        this.currentRoute = routeData;
      });
  }

  ngOnDestroy() {
    this.onDestroy$.next();
    this.onDestroy$.complete();
  }
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Детали нашего текущего маршрута будут присвоены свойству this.currentRoute, что позволит нам использовать данные маршрута для конкретного поста так, как мы хотим.

Стилизация нашего блога

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

ng generate component blog/blog-list/blog-card
Войти в полноэкранный режим Выйти из полноэкранного режима

Откройте файл blog-card.component.html и поместите в него приведенный ниже код

<li *ngFor="let post of links$ | async">
    <a [href]="post.route">{{post.title}}</a>
</li>
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем мы используем этот компонент в нашем blog-list.component.html через селектор app-blog-card таким образом

<div>
  <ul>
    <app-blog-card [post]="post" *ngFor="let post of links$ | async"></app-blog-card>
  </ul>
</div>
Войти в полноэкранный режим Выйти из полноэкранного режима

Мы также передаем каждый объект маршрута в массиве как post через компонент app-blog-card, поэтому нам нужно получить эти данные из родительского компонента (blog list) в нашем дочернем компоненте (blog card) с помощью декоратора Input().
Давайте сделаем это, поместив приведенный ниже код в blog-card.component.ts.

import {Component, Input, OnInit} from '@angular/core';
import {ScullyRoute} from "@scullyio/ng-lib";

@Component({
  selector: 'app-blog-card',
  templateUrl: './blog-card.component.html',
  styleUrls: ['./blog-card.component.scss']
})
export class BlogCardComponent implements OnInit {
  @Input() post: Partial<ScullyRoute> = {};

  constructor() { }

  ngOnInit(): void {
  }
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Наконец, немного стилизации

Вы можете использовать любой формат стилизации, какой захотите. Я буду использовать tailwind.
Давайте воспользуемся простой документацией по tailwind, чтобы настроить его в нашем проекте.
После завершения настройки нам нужно будет перезапустить сервер angular.
Обновите blog-card.component.html новым шаблоном, приведенным ниже.

<li class="h-full">
  <a [href]="post.route" class="h-full w-full lg:max-w-full lg:flex hover:bg-gray-100">
    <div
      class="h-48 lg:h-auto lg:w-48 flex-none bg-cover rounded-t lg:rounded-t-none lg:rounded-l text-center overflow-hidden"
      [style.background-image]="'url(' + (post['image'] || 'https://images.unsplash.com/photo-1496494118863-9cf5d1e70cd6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=870&q=80') + ')'"
      title="Mountain">
    </div>
    <div
      class="w-full border-r border-b border-l border-gray-400 lg:border-l-0 lg:border-t lg:border-gray-400  rounded-b lg:rounded-b-none lg:rounded-r p-4 flex flex-col justify-between leading-normal">
      <div class="mb-8">
        <div class="text-gray-900 font-bold text-xl mb-2">{{post.title}}</div>
        <p class="text-gray-700 text-base">{{post['seo']?.metaDescription || post['description']}}</p>
      </div>
    </div>
  </a>
</li>
Вход в полноэкранный режим Выход из полноэкранного режима

Также обновите blog-list.component.html.

<div>
  <ul class="p-10 grid grid-cols-1 sm:grid-cols-1 md:grid-cols-1 lg:grid-cols-1 xl:grid-cols-3 gap-5">
    <app-blog-card [post]="post" *ngFor="let post of links$ | async"></app-blog-card>
  </ul>
</div>
Войти в полноэкранный режим Выход из полноэкранного режима

Код ниже добавляет заголовок на наш сайт. Добавьте его в app.component.html.

<nav class="flex flex-wrap items-center justify-between bg-gray-800 p-6 w-full">
  <div>
    <a class="text-white no-underline hover:text-white hover:no-underline" routerLink="/">
      <span class="text-2xl">Angular scully blog</span>
    </a>
  </div>
  <ul class="list-reset flex justify-end flex-1 items-center">
    <li class="mr-3">
      <a class="inline-block text-white no-underline hover:text-gray-200 hover:text-underline py-2 px-4"
         routerLinkActive="" routerLink="/blog">blog</a>
    </li>
  </ul>
</nav>
<router-outlet></router-outlet>
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь у нас есть красивый пользовательский интерфейс, который показывает все записи блога.

Добавим приведенный ниже код для пользовательского интерфейса записей блога (blog-post.component.html)

<main class="mx-auto max-w-screen-sm max-w-screen-lg">
  <img [src]="currentRoute['image'] || 'https://cdn.mos.cms.futurecdn.net/4uiRZ5nNAgpHSifSjaKwcC-970-80.jpg.webp'"
       alt="nft"/>
  <h3>ScullyIo content</h3>
  <hr>

  <!-- This is where Scully will inject the static HTML -->
  <scully-content></scully-content>
  <hr>
  <h4>End of content</h4>
</main>
Вход в полноэкранный режим Выйти из полноэкранного режима


Наконец, используйте команду npm run build для сборки проекта и проверки статической версии scully в браузере.

Отправка на Github

Создайте репозиторий на Github, а затем скопируйте удаленный url (https://github.com/<github-username>/<repository-name>.git), чтобы мы могли добавить его для нашего проекта с помощью команды ниже:

git remote add origin https://github.com/<github-username>/<repository-name>.git
Войти в полноэкранный режим Выйти из полноэкранного режима

Переименуйте ветку в main с помощью команды ниже:

git branch -M main
Войти в полноэкранный режим Выйти из полноэкранного режима

Сделайте push на github с помощью команды ниже:

git push origin main
Войти в полноэкранный режим Выйти из полноэкранного режима

Вот ссылка на репозиторий для демонстрации этой статьи.

BrunoElo / angular-scully-blog

Демонстрационное приложение для статьи angular scully blog. Может быть использовано в качестве начального шаблона

AngularBlog

Это репозиторий для демонстрации блога Angular с интеграцией Netlify CMS и Forestry CMS, развернутой на Netlify.

Технологический стек

Инструменты, использованные для создания проекта, включают;

  • Angular версии 13.3
  • Scully
  • Tailwind
  • Netlify CMS
  • Forestry CMS

Начало работы

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

npm install
Войти в полноэкранный режим Выйти из полноэкранного режима

Используйте приведенную ниже команду для запуска команд сборки angular и scully:

npm run build

Подать приложение angular:

ng serve

Подать версию scully, которая является статическим сайтом:

npx scully serve

Перейдите по адресу http://localhost:4200/, чтобы увидеть приложение Angular, и http://localhost:1668/, чтобы увидеть статический сайт scully.

Дополнительная помощь

Для получения дополнительной помощи по Angular CLI используйте ng help или перейдите на страницу обзора и справочника команд Angular CLI.

Посмотреть на GitHub

Развертывание в Netlify

После размещения нашего блога Angular Scully на github мы готовы к развертыванию на Netlify. Если у вас еще нет учетной записи, вы можете зарегистрироваться здесь или войти в свою учетную запись, если она у вас есть.

Выберите импорт из git

Выберите Github

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

Укажите путь к папке, в которой находятся статические страницы, сгенерированные scully, а также команду сборки.


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

Вот ссылка на развернутый сайт

Заключение

Я надеюсь, что эта статья была полезной и помогла вам создать сайт блога с помощью Angular и scully с нуля.
Вы можете оставить вопросы или предложения в комментариях или связаться со мной в твиттере BrunoElo, если хотите. Спасибо за чтение!

Оцените статью
Procodings.ru
Добавить комментарий