Поиск по сайту:

Как создать свой блог с помощью Ghost и Next.js


Автор выбрал программу Write for DOnations.

Введение

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

Хотя Ghost предоставляет шаблоны, которые вы можете использовать для своего внешнего интерфейса, существует ограниченная гибкость для разработки и добавления пользовательских функций. Фреймворк React, который предоставляет решения таких проблем, как оптимизация производства, производительность и SEO. Next.js дает преимущество перед разработчиком при создании приложений с помощью React. Вы можете использовать Next.js с Ghost для создания статически сгенерированного блога с лучшей производительностью и SEO. Вы также можете настроить свой дизайн и добавить нужные функции.

В этом руководстве вы будете использовать Ghost для управления статьями и Next.js для создания внешнего интерфейса вашего блога. Такой подход позволяет вам контролировать содержание, дизайн и функциональность вашего блога. Вы самостоятельно разместите Ghost на сервере Ubuntu, который можно развернуть с помощью DigitalOcean Marketplace.

Предпосылки

Для выполнения этого урока вам понадобятся:

  • Учетная запись DigitalOcean. Если у вас его нет, зарегистрируйте новый аккаунт.
  • Ghost установлен на сервере Ubuntu с объемом памяти не менее 1 ГБ. Вы можете получить документацию Ghost для ручной настройки.
  • Зарегистрированное доменное имя, указывающее на ваш блог Ghost. Инструкции по настройке домена на сервере имен DigitalOcean можно найти в руководстве Как указать на серверы имен DigitalOcean из общих регистраторов доменов.
  • Локальная среда разработки для Node.js. Следуйте инструкциям по установке Node.js и созданию локальной среды разработки.
  • Проект Next.js на локальном компьютере. Вы можете создать проект Next.js на шаге настройки библиотеки в create-next-app и принять настройки по умолчанию.

Примечание. Вы можете создать проект Next.js с помощью TypeScript или JavaScript. В этом руководстве используется JavaScript, но любой из них будет работать.

  • Tailwind установлен на вашем локальном компьютере и настроен для Next.js. После установки Next.js следуйте инструкциям в руководстве по установке Tailwind CSS с помощью Next.js.
  • Текстовый редактор, например nano.
  • Знакомство с React, которое вы можете получить из серии How To Code in React.js.

Шаг 1 — Публикация постов на Ghost

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

После установки и настройки Ghost вам потребуется учетная запись администратора для управления вашим блогом. Перейдите к ВАШ_ДОМЕН/ghost, где ВАШ_ДОМЕН — это URL-адрес, введенный вами в процессе установки Ghost.

Вы увидите страницу «Добро пожаловать в Ghost», где вам будет предложено создать учетную запись. Введите название вашего сайта, ваше имя, адрес электронной почты и пароль. Затем нажмите «Создать аккаунт» и начните публикацию.

На левой боковой панели в разделе «Что вы хотите сделать в первую очередь?» выберите параметр «Написать свой первый пост». Введите заголовок и текст сообщения в блоге и нажмите «Опубликовать» в правом верхнем углу. Нажмите «Продолжить», окончательный обзор, а затем нажмите кнопку «Опубликовать сообщение прямо сейчас».

Перейдите к ВАШ_ДОМЕН/ghost, чтобы просмотреть панель управления. На левой боковой панели выберите «Просмотреть сайт», чтобы увидеть предварительный просмотр вашего сайта в реальном времени:

Позже в этом руководстве вы создадите несколько сообщений блога на своем веб-сайте. На панели инструментов выберите «Сообщения» на левой боковой панели, чтобы создать еще два сообщения в блоге. Дайте им уникальные имена, чтобы вы могли узнать их позже.

На этом шаге вы настроили свою страницу администратора в Ghost и опубликовали сообщения в блоге. Ваш блог в настоящее время использует шаблон по умолчанию, предоставленный Ghost. На следующем шаге вы создадите проект Next.js, который обеспечит вам большую гибкость дизайна. Вы будете использовать Ghost в качестве системы управления контентом (CMS) и Next.js для создания внешнего интерфейса.

Шаг 2 — Создание проекта Next.js

Теперь у вас есть Ghost, готовый для вашего блога. На этом этапе вы создадите интерфейс для своего блога с помощью Next.js и добавите к нему стиль с помощью Tailwind CSS.

Откройте созданный вами проект Next.js в предпочитаемом вами редакторе кода. Откройте файл pages/index.js:

  1. nano pages/index.js

В файле удалите существующий код и добавьте следующее, чтобы импортировать компонент Head из next/head и создать компонент React:

import Head from 'next/head';

export default function Home() {
	return (
	);
}

Компонент Head позволяет добавлять такие теги, как <title> и <meta>, которые вы используете в HTML.

Чтобы использовать компонент Head, добавьте оператор return, содержащий тег <div>. Внутри тега <div> добавьте тег , который принимает <title> и <meta> тег:

...
return (
	<div>
	     <Head>
	            <title>My First Blog</title>
	            <meta name="description" content="My personal blog created with Next.js and Ghost" />
            </Head>
	</div>
);
...

Тег <title> устанавливает заголовок вашей веб-страницы как «Мой первый блог». Тег <meta> имеет атрибут name, установленный как description, и атрибут content, установленный как Мой личный блог, созданный с помощью Next.js и Ghost. Этот заголовок и описание будут отображаться в предварительном просмотре вашего веб-сайта.

Теперь, когда вы установили заголовок и описание, вы отобразите список статей. Этот фрагмент кода пока использует фиктивные данные. Вы получите сообщения в блоге от Ghost в следующем разделе. Под тегом добавьте список статей, используя теги <li>:

...
<div>
	<Head>
	...
	</Head>
	<main className="container mx-auto py-10">
		<h1 className="text-center text-3xl">My Personal Blog</h1>
		<div className="flex justify-center mt-10 ">
			<ul className="text-xl">
				<li>How to build and deploy your blog on DigitalOcean</li>
				<li>How to style a Next.js website</li>
				<li>How to cross-post your articles automatically</li>
			</ul>
		</div>
	</main>
</div>
...

Теперь вы добавили тег <main>, который содержит заголовок и тег <div>. Тег <div> содержит неупорядоченный список с несколькими фиктивными заголовками статей. Теги <main>, <h1>, <div> и <ul> включают в себя Атрибут имя_класса. Они содержат классы стиля Tailwind.

Файл index.js теперь должен выглядеть следующим образом:

import Head from 'next/head';

export default function Home() {
	return (
		<div>
			<Head>
				<title>My First Blogp</title>
				<meta
					name="description"
					content="My personal blog created with Next.js and Ghost"
				/>
			</Head>
			<main className="container mx-auto py-10">
				<h1 className="text-center text-3xl">My Personal Blog</h1>
				<div className="flex justify-center mt-10 ">
					<ul className="text-xl">
						<li>How to build and deploy your blog on DigitalOcean</li>
						<li>How to style a Next.js website</li>
						<li>How to cross-post your articles automatically</li>
					</ul>
				</div>
			</main>
		</div>
	);
}

Сохраните и закройте файл.

Запустите веб-сервер локально. Если вы используете Yarn, выполните следующую команду:

  1. yarn dev

Если вы используете npm, выполните следующую команду:

  1. npm run dev

Перейдите к https://localhost:3000 в своем браузере, и вы должны найти список статей блога из фиктивных данных. Ваша домашняя страница будет выглядеть примерно так:

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

Шаг 3 — Получение всех сообщений в блоге от Ghost

На этом шаге вы получите сообщения блога, созданные в Ghost, и отобразите их в браузере.

Чтобы получить свои статьи из Ghost, вы должны сначала установить библиотеку JavaScript для Ghost Content API. Остановите сервер с помощью сочетания клавиш CTRL+C. Выполните следующую команду в терминале, чтобы установить библиотеку:

Если вы используете Yarn, выполните следующую команду:

  1. yarn add @tryghost/content-api

Если вы используете npm, выполните следующую команду:

  1. npm i @tryghost/content-api

После успешной установки библиотеки вы теперь создадите файл для хранения логики для получения ваших сообщений в блоге. В каталоге pages создайте новую папку с именем utils:

  1. mkdir utils

Создайте в этой папке новый файл с именем ghost.js:

  1. nano pages/utils/ghost.js

В файле ghost.js импортируйте модуль GhostContentAPI из библиотеки Ghost Content API. Инициализируйте новый объект для GhostContentAPI и сохраните значение в постоянной переменной api. Вам нужно будет передать значения для хоста, ключа API и версии. Код выглядит следующим образом:

import GhostContentAPI from "@tryghost/content-api";

const api = new GhostContentAPI({
	url: `YOUR_URL`,
	key: `YOUR_API_KEY`,
	version: 'v5.0'
});

Значение ВАШ_URL — это имя домена, которое вы настроили при установке Ghost, включая протокол, скорее всего, https://.

Чтобы найти ключ Ghost API, выполните следующие действия:

  1. Перейдите к ВАШ_ДОМЕН/ghost (где ВАШ_ДОМЕН — это URL-адрес, который вы настроили) и войдите в систему, используя свой учетные данные администратора.
  2. Нажмите значок шестеренки в нижней части левой боковой панели, чтобы открыть страницу настроек.
  3. В категории «Дополнительно» нажмите «Интеграции» на левой боковой панели. Откроется страница Интеграции со списком возможных интеграций и пользовательских интеграций внизу.
  4. Нажмите + Добавить пользовательскую интеграцию. Появится всплывающее окно с предложением назвать вашу интеграцию.
  5. Введите имя интеграции в поле «Имя» и нажмите «Создать». Вы перейдете на страницу для настройки пользовательской интеграции.
  6. Скопируйте отображаемый ключ Content API (не ключ API администратора).
  7. Нажмите Сохранить, чтобы сохранить пользовательскую интеграцию.

В файле ghost.js замените YOUR_API_KEY скопированным ключом API.

Теперь, когда вы инициализировали GhostContentAPI, вы напишете асинхронную функцию для получения всех статей из вашей установки Ghost. Эта функция извлекает сообщения блога без учета их тегов. Скопируйте и вставьте следующий код в файл ghost.js:

...
export async function getPosts() {
  return await api.posts
    .browse({
        include:"tags",
        limit: "all"
    })
    .catch(err => {
      console.error(err);
    });
}

После разрешения промиса асинхронная функция getPosts() вернет сообщения в блоге. Он использует метод posts.browse() из GhostContentAPI и принимает параметры include и limit. Вы установили значение include в tags, чтобы получать теги вместе с содержимым. Значение limit установлено равным all, чтобы получить все сообщения блога. Если возникает ошибка, она регистрируется в консоли браузера.

На данный момент файл api/ghost.js содержит следующий код:

import GhostContentAPI from '@tryghost/content-api';

const api = new GhostContentAPI({
	url: `YOUR_URL`,
	key: `YOUR_API_KEY`,
	version: 'v5.0',
});

export async function getPosts() {
	return await api.posts
		.browse({
			include: 'tags',
			limit: 'all',
		})
		.catch((err) => {
			console.error(err);
		});
}

Сохраните и закройте файл.

Чтобы отобразить список сообщений, откройте файл index.js. Добавьте следующую выделенную строку, чтобы импортировать функцию getPosts над импортом Head:

import { getPosts } from './utils/ghost';
import Head from 'next/head';

…

Теперь вы создадите асинхронную функцию (getStaticProps()), которая позволит Next.js предварительно отображать страницу во время сборки. Эта функция полезна, когда вы хотите воспользоваться преимуществами статической генерации.

После компонента Home создайте асинхронную функцию getStaticProps() и вызовите метод getPosts() внутри тела функции. Возвращает значение props из метода getPosts(). Код должен выглядеть следующим образом:

...
export async function getStaticProps() {
	const posts = await getPosts();
	return { props: { posts } };
}

Сохраните файл.

Теперь, когда ваш метод getStaticProps() определен, перезапустите сервер, используя npm run dev или yarn dev, если вы используете Yarn.

В вашем браузере страница по-прежнему показывает статические данные. На странице не отображаются данные, которые вы получили от Ghost. Это потому, что вы извлекаете значения, но не отображаете их.

Вы внесете некоторые изменения в index.js, чтобы ваш компонент Home мог использовать значения, которые вы получаете от Ghost.

Нажмите CTRL-C, чтобы остановить сервер, затем откройте index.js для редактирования:

  1. nano pages/index.js

Внесите следующие выделенные изменения в index.js:

export default function Home({posts}) {
	return (
		<div>
			<Head>
				<title>My First Blog</title>
				<meta
					name="description"
					content="My personal blog created with Next.js and Ghost"
				/>
			</Head>
			<main className="container mx-auto py-10">
				<h1 className="text-center text-3xl">My Personal Blog</h1>
				<div className="flex justify-center mt-10 ">
					<ul className="text-xl">
						{posts.map((post) => (
							<li key={post.title}>{post.title}</li>
						))}
					</ul>
				</div>
			</main>
		</div>
	);
}

В этом коде сначала вы деструктурируете и передаете posts в качестве параметра компоненту Home. Затем вы заменяете теги <li>, содержащие статический контент, который вы использовали для насмешек, функцией для получения заголовков сообщений в блоге.

Вы используете метод array.map() для перебора коллекции posts, полученной из props, чтобы получить title каждого поста. Наконец, вы возвращаете тег <li>, содержащий заголовок сообщения.

Сохраните и закройте файл. Перезапустите сервер с помощью npm run dev или yarn dev, если используется Yarn.

Вернитесь к localhost:3000. Теперь ваш блог отображает список статей из Ghost.

Код для вашего файла index.js будет соответствовать следующему:

import { getPosts } from './utils/ghost';
import Head from 'next/head';

export default function Home({ posts }) {
	return (
		<div>
			<Head>
				<title>My First Blog</title>
				<meta
					name="description"
					content="My personal blog created with Next.js and Ghost"
				/>
			</Head>
			<main className="container mx-auto py-10">
				<h1 className="text-center text-3xl">My Personal Blog</h1>
				<div className="flex justify-center mt-10 ">
					<ul className="text-xl">
						{posts.map((post) => (
							<li key={post.title}>{post.title}</li>
						))}
					</ul>
				</div>
			</main>
		</div>
	);
}

export async function getStaticProps() {
	const posts = await getPosts();
	return { props: { posts } };
}

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

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

Шаг 4 — Рендеринг каждого отдельного поста

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

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

Для создания динамических маршрутов и отображения отдельных сообщений вам необходимо:

  1. Напишите функцию для получения содержимого сообщения в блоге.
  2. Создавайте динамические маршруты.
  3. Добавьте ссылки на блог в список элементов.

В файле ghost.js вы написали функцию getPosts(), чтобы получить список всех сообщений вашего блога. Теперь вы напишете функцию getSinglePost(), которая извлекает содержимое вашего сообщения на основе слага.

Ghost автоматически создает ярлык для вашей статьи, используя заголовок. Например, если ваша статья называется «Мой первый пост», Ghost сгенерирует my-first-post в качестве слага. Этот слаг помогает идентифицировать статью и может быть добавлен к URL-адресу вашего домена, чтобы отображать содержимое.

Функция getSinglePost() примет postSlug в качестве параметра и вернет содержимое соответствующего сообщения в блоге.

Остановите сервер, если он все еще работает, затем откройте pages/utils/ghost.js для редактирования.

Под функцией getPosts() в файле ghost.js добавьте и экспортируйте функцию async getSinglePost(). .

...
export async function getSinglePost(postSlug) {
    return await api.posts
      .read({
        slug: postSlug
      })
      .catch(err => {
        console.error(err);
      });
  }

Функция getSinglePost() использует метод posts.read() интерфейса GhostContentAPI и передает параметр postSlug в метод posts.read().

Окончательный файл /utils/ghost.js содержит следующий код:

import GhostContentAPI from '@tryghost/content-api';

const api = new GhostContentAPI({
	url: `YOUR_URL`,
	key: `YOUR_API_KEY`,
	version: 'v5.0',
});

export async function getPosts() {
	return await api.posts
		.browse({
			include: 'tags',
			limit: 'all',
		})
		.catch((err) => {
			console.error(err);
		});
}

export async function getSinglePost(postSlug) {
    return await api.posts
      .read({
        slug: postSlug
      })
      .catch(err => {
        console.error(err);
      });
  }

Сохраните и закройте файл.

В Next.js вы можете добавить скобки к имени файла ([param]), чтобы создать динамический маршрут. Например, /post/[slug].js создаст динамические маршруты.

В каталоге pages создайте новый файл с именем /post/[slug].js:

nano pages/post/\[slug\].js

Примечание. Команда nano показывает, что скобки отделены обратной косой чертой (\), которая необходима bash для создания файла со скобками в имени.

Имя файла [slug].js будет соответствовать любой строке символов после пути /post/. Next.js создаст страницы для всех ваших сообщений во время сборки.

В файле /post/[slug].js импортируйте функции getPosts() и getSinglePost() из файла . ./utils/ghost.js.

Файл /post/[slug].js также содержит шаблон для сообщений. Добавьте следующий код в файл /post/[slug].js:

import { getPosts, getSinglePost } from '../utils/ghost';

export default function PostTemplate(props) {
	const { post } = props;
	const { title, html, feature_image } = post;
	return (
		<main className="container mx-auto py-10">
			<h1 className="text-center text-3xl">{title}</h1>
			<article
				className="mt-10 leading-7 text-justify"
				dangerouslySetInnerHTML={{ __html: html }}
			/>
		</main>
	);
}

export const getStaticProps = async ({ params }) => {
	const post = await getSinglePost(params.slug);
	return {
		props: { post },
	};
};

Функция PostTemplate() создает функциональный компонент. В этой функции вы извлекаете объект post из props и извлекаете title, html и feature_image из объекта post. Компонент возвращает HTML, который используется для создания страниц.

Поскольку Ghost API возвращает содержимое блога в формате HTML, тег <article> содержит атрибут dangerouslySetInnerHTML со значением HTML, извлеченным из объекта post. . Атрибут dangerouslySetInnerHTML — это замена React для использования innerHTML в DOM браузера. Если HTML исходит из ненадежного источника, вы должны очистить HTML перед передачей его атрибуту dangerouslySetInnerHTML.

Асинхронная функция getStaticProps() извлекает содержимое сообщения блога, соответствующее слагу.

Чтобы правильно сопоставить содержимое с URL-адресом, Next.js необходимо знать значение путей (slug в вашем случае). Это можно сделать с помощью функции getStaticPaths(). Подобно функции getStaticProps(), напишите асинхронную функцию с именем getStaticPaths(), чтобы возвращать список слагов. Добавьте следующую функцию в конец файла [slug].js:

...
export const getStaticPaths = async () => {
	const allPosts = await getPosts();
	return {
		paths: allPosts.map(({ slug }) => {
			return {
				params: { slug },
			};
		}),
		fallback: false,
	};
};

Примечание. Если установить для fallback значение false, все пути, которые не будут возвращены getStaticPaths(), приведут к странице 404.

Файл /post/[slug].js должен выглядеть следующим образом:

import { getPosts, getSinglePost } from '../utils/ghost';

export default function PostTemplate(props) {
	const { post } = props;
	const { title, html, feature_image } = post;
	return (
		<main className="container mx-auto py-10">
			<h1 className="text-center text-3xl">{title}</h1>
			<article
				className="mt-10 leading-7 text-justify"
				dangerouslySetInnerHTML={{ __html: html }}
			/>
		</main>
	);
}

export const getStaticProps = async ({ params }) => {
	const post = await getSinglePost(params.slug);
	return {
		props: { post },
	};
};

export const getStaticPaths = async () => {
	const allPosts = await getPosts();
	return {
		paths: allPosts.map(({ slug }) => {
			return {
				params: { slug },
			};
		}),
		fallback: false,
	};
};

Сохраните и закройте файл. Перезапустите сервер с помощью npm run dev или yarn dev, если используется Yarn.

Перейдите к localhost:3000/post/SLUG, заменив SLUG слагом, соответствующим одному из ваших сообщений в блоге. . Теперь вы можете получить доступ к содержимому блога для сообщения, соответствующего <^, отображаемому в браузере.

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

Остановите сервер, если он все еще работает, и снова откройте pages/index.js для редактирования.

Отредактируйте выделенный раздел index.js следующим образом, добавив импорт и компонент Link:

import { getPosts } from './utils/ghost';
import Head from 'next/head';
import Link from 'next/link';

export default function Home(props) {
	return (
		<div>
			<Head>
				<title>My First Blog</title>
				<meta
					name="description"
					content="My personal blog created with Next.js and Ghost"
				/>
			</Head>
			<main className="container mx-auto py-10">
				<h1 className="text-center text-3xl">My Personal Blog</h1>
				<div className="flex justify-center mt-10 ">
				<ul className="text-xl">
					{props.posts.map((post) => (
						<li key={post.title}>
							<Link href={`post/${post.slug}`}>{post.title}</Link>
						</li>
					))}
				</ul>
				</div>
			</main>
		</div>
	);
}

export async function getStaticProps() {
	const posts = await getPosts();
	return { props: { posts } };
}

Next.js предоставляет компонент Link, который обеспечивает маршрутизацию на стороне клиента. В файле index.js вы импортируете компонент Link из next/link. Внутри тега <li> вы вкладываете заголовок сообщения в компонент Link. Как и тег привязки в HTML, тег Link также принимает атрибут href. Вы передаете slug как значение атрибута href.

Сохраните файл и перезапустите сервер разработки, выполнив команду npm run dev или yarn dev.

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

Ваша страница сообщения появится с заголовком и содержимым тела, которые вы указали на шаге 1. Показанный здесь образец называется «Как создать и развернуть свой блог в DigitalOcean» с содержимым из введения в этот учебник.

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

Заключение

В этой статье вы развернули Ghost в качестве CMS в дроплете DigitalOcean. Вы создали свой собственный статически сгенерированный сайт с помощью Next.js и подключили CMS Ghost для предоставления контента для вашего сайта Next.js. Поскольку это статически сгенерированный веб-сайт, вы отправляете меньше JavaScript на стороне клиента, что приводит к повышению производительности и SEO. Вы также использовали TailwindCSS для добавления индивидуального дизайна в свой блог.

В настоящее время ваш блог работает локально на вашем компьютере. В качестве следующего шага вы можете развернуть приложение Next.js на платформе приложений.

Вы также можете добавить дополнительные функции в свой блог. Например, вы можете реализовать функцию поиска с помощью тегов.