Это вторая часть серии статей «Создание личного сайта-блога». В этой серии мы настроим бесплатную CMS для хранения контента блога на Railway, создадим приложение React с помощью Next.js для генерации статических сайтов и TailwindCSS для представления статей и разместим его на Netlify.
Все статьи:
Часть 1: Размещение бесплатной Strapi CMS на Railway
Часть 2: Настройка базового приложения Next.js локально
Теперь, когда базовая CMS установлена на Railway и Cloudinary настроен для работы с изображениями, следующим шагом будет… создание приложения Next.
Это приложение Next будет потреблять контент, предоставляемый CMS, и представлять его на веб-странице. В этой части цикла будет создана и локально настроена базовая домашняя страница со списком записей блога.
Так что давайте просто перейдем к этому. Первое, что вам нужно создать, это новое приложение Next.js. Это можно сделать с помощью этой простой команды:
npx create-next-app@latest
После ее выполнения появится запрос на имя приложения — укажите его, и проект будет создан.
Прежде чем начать работу над приложением, давайте установим некоторые зависимости, которые пригодятся в дальнейшем — TailwindCSS для стилизации приложения и Apollo Client для работы с GraphQL.
Сначала установим TailwindCSS.
yarn add tailwindcss postcss autoprefixer
npx tailwindcss init -p
После выполнения команды init будет создан файл tailwind.config.js
. Вам нужно внести несколько изменений в созданный файл, чтобы все работало правильно.
Замените пустой массив content
на:
content: [
"./pages/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
Также необходимо импортировать правила TailwindCSS в файл ./styles/globals.css
:
@tailwind base;
@tailwind components;
@tailwind utilities;
Теперь вы закончили с настройкой TailwindCSS. Давайте перейдем к клиенту Apollo.
yarn add @apollo/client graphql
Вот и все! Вы готовы создать свою страницу блога, управляемую GraphQL.
Сначала вам нужно настроить Apollo Client, который будет работать как «шлюз» к вашему GraphQL API. Создайте новый файл в корне проекта под названием apollo-client.js (вы можете назвать его как угодно, TBH).
import { ApolloClient, InMemoryCache } from "@apollo/client";
const client = new ApolloClient({
uri: "https://[YOUR-CMS-GRAPHQL-URL]",
cache: new InMemoryCache(),
});
export default client;
Когда все готово, пришло время использовать это! Для начала вам нужно получить список всех записей блога, которые будут отображаться на главной странице. Вы будете использовать функцию Static Site Generation в Next.js — для этого необходим метод getStaticProps
.
Побочное замечание — почему именно SSG?
Поскольку в этой серии уроков мы говорим о бесплатной настройке блога, мы ограничены в производительности. CMS, установленная на бесплатном Railway tier, не является демоном скорости, приложение Next.js с SSR, установленное на бесплатном Netlify, также будет довольно медленным. Таким образом, SSG кажется лучшим вариантом, даже если каждое изменение в нашем блоге потребует перестройки приложения. Если бюджет не является для вас проблемой, то вы можете подумать о других вариантах, но это не тема этой серии.
Прежде чем мы продолжим, давайте добавим еще два поля в конфигурацию записи блога. Откройте репозиторий для вашей Strapi CMS и запустите его в режиме разработки (yarn develop
). Войдите в систему и перейдите к конструктору Content-Type Builder. Сначала вам нужно будет создать новый тип с именем Tag.
У него есть ID и имя, которое должно быть уникальным.
Когда это будет сделано, вы будете готовы «обновить» тип записи Blog. В Content-Type Builder откройте Post и добавьте два новых поля. Первое — это excerpt — короткий текст, который будет представлен на вашей домашней странице. Оно должно быть типа string и желательно иметь некоторую максимальную длину (в моем случае 250 символов). Он также должен быть обязательным.
Теперь необходимо добавить Tags. Это будет поле типа Relation, а отношение будет Many-to-Many, так как один пост может иметь несколько тегов, и каждый тег может принадлежать нескольким постам. Сделайте это следующим образом:
Теперь зайдите в терминал и зафиксируйте все изменения, которые были сделаны в ваших конфигурационных файлах Strapi. А затем запустите изменения для восстановления CMS, размещенной на Railway:
git push
Когда приложение на Railway будет восстановлено — войдите в систему и измените разрешения пользователя Public, чтобы вы могли запрашивать Tags.
Теперь вы можете получить данные в getStaticProps
в файле index.js
вашего приложения Next.js. Добавьте это в нижней части pages/index.js
.
export async function getStaticProps() {
const { data } = await client.query({
query: gql`
query Posts {
posts(
sort: "publishedAt:desc"
pagination: { limit: 5 }
filters: { publishedAt: { notNull: true } }
) {
data {
attributes {
title
slug
cover {
data {
attributes {
url
}
}
}
excerpt
tags {
data {
attributes {
tagId
name
}
}
}
}
}
}
}
`,
});
return {
props: {
posts: data.posts.data,
},
};
}
Что это делает? При каждой сборке (yarn build
) Next будет выполнять этот запрос перед «рендерингом» страницы/созданием статического вывода страницы. Это означает, что вам не понадобятся индикаторы загрузки на вашей веб-странице, потому что данные уже будут получены. Недостатком этого, конечно, является то, что вам придется перестраивать приложение при каждом изменении содержимого в CMS. Но для персональных блогов это довольно хорошее и дешевое решение.
Вы загружаете только самые важные данные, которые будут показаны на домашней странице. Итак, давайте создадим простую домашнюю страницу.
Вы начнете с создания нового компонента — карточки для предварительного просмотра записи в блоге. Создайте новый файл components/BlogPostPreview.jsx
(если нет папки component
— создайте ее).
import Image from "next/image";
import React from "react";
const BlogPostPreview = ({ post }) => {
return (
<div className="max-w-sm rounded overflow-hidden shadow-lg">
<a href="#">
<Image
className="w-full"
src={post.attributes.cover.data.attributes.url}
alt=""
width={1000}
height={480}
objectFit="cover"
/>
<div className="px-6 py-4">
<div className="font-bold text-xl mb-2">{post.attributes.title}</div>
<p className="text-gray-700 text-base">{post.attributes.excerpt}</p>
</div>
</a>
<div className="px-6 pt-4 pb-2">
{post.attributes.tags.data.map((tag) => (
<a href="#" key={tag.attributes.tagId}>
<span className="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">
{tag.attributes.name}
</span>
</a>
))}
</div>
</div>
);
};
export default BlogPostPreview;
Здесь вы получаете один prop
— post. В нем содержится вся найденная информация. Затем мы создаем простую карточку с изображением обложки, заголовком, отрывком и списком тегов. Конечный результат будет выглядеть примерно так:
Как видите, в коде есть несколько пустых тегов a
— на последующих этапах этой серии уроков вы будете их использовать, не волнуйтесь.
Теперь, наконец, пришло время показать посты вашего блога. В pages/index.js
используйте только что созданный компонент.
export default function Home({ posts }) {
return (
<div className="flex flex-col items-center">
<h1 className="text-3xl uppercase font-serif font-bold my-8">
My personal blog
</h1>
<section className="grid grid-cols-3 gap-4">
{posts.map((post) => (
<BlogPostPreview post={post} key={post.attributes.slug} />
))}
</section>
</div>
);
}
Еще одна вещь, прежде чем вы создадите этот компонент — если вы используете Cloudinary для изображений (как это было сделано во второй части этой серии), вам нужно будет добавить домен Cloudinary в next.config.js
. Это необходимо для того, чтобы вы могли использовать компонент next/image
вместо тега img
.
Ух ты, это была отличная поездка. Давайте, наконец, построим это! Запустите
Конечно, не забудьте создать несколько тегов и постов (вам нужно будет создать тег, прежде чем вы сможете использовать его в посте). Также, поскольку вы добавили несколько новых обязательных полей — обновите существующие посты, чтобы в них была заполнена вся необходимая информация.
Вот и все. Это конец второй части этой серии уроков. В следующей части вы создадите страницу записей блога. До встречи!