Мы создадим сайт блога с помощью фреймворка Angular, Scully для генерации статических сайтов (SSG), а затем развернем проект на Netlify, чтобы он был доступен для публики.
- О чем пойдет речь в этой статье
- Требования к настройке
- Генерация проекта Angular
- Установка Scully
- Добавление поддержки блогов в наш проект
- Несколько других конфигураций
- Настройка точки входа маршрута для scully
- Создание приложения Angular-Scully
- Создание записей блога
- Совет по выбору команды для сборки
- Показ всех записей блога
- Доступ к данным на странице записи блога
- Стилизация нашего блога
- Наконец, немного стилизации
- Отправка на Github
- BrunoElo / angular-scully-blog
- Демонстрационное приложение для статьи angular scully blog. Может быть использовано в качестве начального шаблона
- AngularBlog
- Технологический стек
- Начало работы
- Дополнительная помощь
- Развертывание в 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
Теперь наша страница должна выглядеть следующим образом.
Вы можете щелкнуть по ссылкам, чтобы увидеть, что они показывают соответствующие посты.
Доступ к данным на странице записи блога
Мы можем получить данные маршрута конкретной страницы, на которую мы переходим, с помощью 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.
Развертывание в Netlify
После размещения нашего блога Angular Scully на github мы готовы к развертыванию на Netlify. Если у вас еще нет учетной записи, вы можете зарегистрироваться здесь или войти в свою учетную запись, если она у вас есть.
Выберите импорт из git
Выберите Github
Подключите репозиторий, который вы хотите развернуть. Вам может потребоваться настроить netlify для вашей учетной записи github, если она еще не была настроена. Это необходимо для того, чтобы netlify имел доступ к репозиторию.
Укажите путь к папке, в которой находятся статические страницы, сгенерированные scully, а также команду сборки.
Нажмите кнопку Deploy site
и наблюдайте, как netlify собирает ваш проект.
Поздравляем, что вы дошли до конца, и получайте удовольствие, персонализируя свой сайт по своему усмотрению.
Вот ссылка на развернутый сайт
Заключение
Я надеюсь, что эта статья была полезной и помогла вам создать сайт блога с помощью Angular и scully с нуля.
Вы можете оставить вопросы или предложения в комментариях или связаться со мной в твиттере BrunoElo, если хотите. Спасибо за чтение!