从零开始:Ghost+Next.js在Silicon Cloud上搭建个人博客详细教程
引言
博客是一种与他人分享你的知识、经验或新闻的媒介。Ghost 是一个开源平台,允许你构建和运行现代化的博客或出版物。
虽然 Ghost 提供了前端使用的模板,但在设计和添加自定义功能方面有一定的限制。Next.js 是一个基于 React 的框架,它为生产优化、性能和 SEO 等问题提供解决方案。相比之下,Next.js 在开发人员体验方面具有优势,可用于构建 React 应用程序。你可以与 Ghost 结合使用 Next.js 来构建具有更好性能和 SEO 的静态生成博客。此外,你还可以自定义设计并添加你想要的功能。
在本教程中,您将使用Ghost管理文章,并使用Next.js构建博客的前端。这种方法使您能够控制博客的内容、设计和功能。您将在Ubuntu服务器上自行托管Ghost,您可以使用Silicon Cloud Marketplace上的Ghost一键式Droplet进行部署。
先决条件
完成本教程,您需要以下物品:
- 一个Silicon Cloud账户。如果您还没有,请注册一个新账户。
- 在至少有1 GB内存的Ubuntu服务器上安装的Ghost。您可以在Silicon Cloud上部署Ghost一键式Droplet,或按照Ghost文档进行手动设置。
- 一个已注册的域名,指向您的Ghost博客。您可以在《如何从常用域名注册商指向Silicon Cloud名称服务器》指南中找到在Silicon Cloud名称服务器上设置域名的说明。
- 一个用于Node.js的本地开发环境。请遵循《如何安装Node.js并创建本地开发环境》。
- 在本地机器上的Next.js项目。您可以通过《入门指南》中的库设置步骤创建Next.js项目。使用create-next-app并接受默认配置。
注意:
您可以使用TypeScript或JavaScript创建Next.js项目。本教程使用JavaScript,但两种语言都可以使用。
- 在本地机器上安装Tailwind,并为Next.js进行配置。安装Next.js后,请遵循《在Next.js中安装Tailwind CSS》指南中的说明。
- 一个文本编辑器,如Visual Studio Code。本教程使用nano。
- 熟悉React,您可以通过《React.js编程系列》获得相关知识。
步骤1 — 在Ghost上发布帖子
在这一步中,您将在Ghost上创建一个帐户并发布一些示例文章,这样将来您在后续步骤中便有内容可呈现。
安装并设置了Ghost后,您将需要一个管理员帐户来管理您的博客。导航到YOUR_DOMAIN/ghost,其中YOUR_DOMAIN是您在Ghost安装过程中输入的URL。
你会看到一个”欢迎来到Ghost”的页面,在那里你将被邀请创建一个账户。输入站点的标题、你的姓名、电子邮箱地址和密码。然后点击”创建账户并开始发布”按钮。
在左侧边栏中,在”您首先想要做什么?”下面,选择”撰写您的第一篇帖子”选项。输入博客文章的标题和内容,并点击右上角的”发布”。接着点击”继续”,最终审阅后点击”立即发布帖子”。
导航到YOUR_DOMAIN/ghost以查看您的仪表板。在左侧边栏中,选择”查看站点”以查看您网站的实时预览。

在本教程的后面,您将在您的网站上发布几篇博客文章。从仪表板中,选择左侧边栏的”帖子”以创建两篇更多的博客文章。给它们起独特的名称,以便您之后能够辨认它们。
在这一步中,您将在Ghost上设置您的管理页面并发布博客文章。您的博客目前正在使用Ghost提供的默认模板。在下一步中,您将创建一个Next.js项目,这将为您提供更多的设计灵活性。您将使用Ghost作为内容管理系统(CMS),使用Next.js来构建前端。
第二步 – 创建一个Next.js项目
现在你已经准备好为你的博客使用Ghost了。在这一步中,你将使用Next.js为你的博客创建前端,并使用Tailwind CSS为其添加样式。
在你偏好的代码编辑器中打开你创建的Next.js项目。打开pages/index.js文件。
- nano pages/index.js
在文件中,删除现有的代码,然后添加以下内容来从next/head导入Head组件并创建一个React组件。
import Head from 'next/head';
export default function Home() {
return (
);
}
Head 组件允许您在 HTML 中添加诸如
在 Head 组件的基础上,添加一个包含
...
return (
<div>
<Head>
<title>My First Blog</title>
<meta name="description" content="My personal blog created with Next.js and Ghost" />
</Head>
</div>
);
...
现在您已经设置了标题和描述,您将显示一个文章列表。这段代码片段暂时使用虚拟数据。在
标签下面,使用...
<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 Silicon Cloud</li>
<li>How to style a Next.js website</li>
<li>How to cross-post your articles automatically</li>
</ul>
</div>
</main>
</div>
...
你现在已经添加了一个包含标题和
、
和
标签都包括className属性。这些属性包含了 Tailwind的样式类。
现在,index.js文件应该如下所示:
页面 / 首页 / 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 Silicon Cloud</li>
<li>How to style a Next.js website</li>
<li>How to cross-post your articles automatically</li>
</ul>
</div>
</main>
</div>
);
}
保存并关闭文件。
在本地启动Web服务器。如果您使用的是Yarn,请执行以下命令:
- yarn dev
如果你正在使用npm,请运行以下命令:
- npm run dev
在浏览器中导航到https://localhost:3000,你会发现一个从模拟数据中获取的博客文章列表。你的首页将以类似以下的方式显示:
在这一步中,您创建了博客的主页并添加了一系列模拟文章。在下一步中,您将从Ghost中拉取博客文章。
第三步 – 从Ghost获取所有博客文章
在这一步骤中,你将从Ghost中获取你创建的博客文章,并在浏览器上呈现它们。
要从Ghost获取您的文章,您首先必须安装Ghost内容API的JavaScript库。使用键盘快捷键CTRL+C停止服务器。在终端中运行以下命令安装该库:
如果你正在使用Yarn,请运行以下命令:
- yarn add @tryghost/content-api
如果你正在使用npm,运行以下命令:
- npm i @tryghost/content-api
在成功安装了该库之后,你现在将创建一个文件来存储获取博客文章的逻辑。在pages目录下,创建一个名为utils的新文件夹。
- mkdir utils
在该文件夹内创建一个名为ghost.js的新文件。
- nano pages/utils/ghost.js
在ghost.js文件中,从Ghost Content API库中导入GhostContentAPI模块。初始化一个新的GhostContentAPI对象并将其值存储在常量变量api中。您需要传递主机、API密钥和版本的值。代码如下所示:
utils/ghost.js
的中文解释
import GhostContentAPI from "@tryghost/content-api";
const api = new GhostContentAPI({
url: `YOUR_URL`,
key: `YOUR_API_KEY`,
version: 'v5.0'
});
YOUR_URL的值是您在安装Ghost时配置的域名,包括协议,很可能是https://。
要找到您的Ghost API密钥,请按照以下步骤操作:
- 导航到YOUR_DOMAIN/ghost(YOUR_DOMAIN是您配置的网址),然后使用管理员凭据登录。
- 点击左侧边栏底部的齿轮图标以访问设置页面。
- 在高级类别中,点击左侧边栏上的集成。集成页面将显示可能的集成列表和自定义集成。
- 点击+ 添加自定义集成。将弹出一个窗口询问您要为集成命名。
- 在名称字段中输入集成的名称,然后点击创建。这将带您到配置自定义集成的页面。
- 复制显示的内容API密钥(不是管理员API密钥)。
- 点击保存以保存自定义集成。
在 ghost.js 文件中,将 YOUR_API_KEY 替换为复制的 API 密钥。
现在,您已经初始化了GhostContentAPI,您将编写一个异步函数来从您的Ghost安装中获取所有文章。该函数会获取博客文章而不考虑它们的标签。请将以下代码复制并粘贴到您的ghost.js文件中:
功能/ghost.js
...
export async function getPosts() {
return await api.posts
.browse({
include:"tags",
limit: "all"
})
.catch(err => {
console.error(err);
});
}
承诺得到解决后,getPosts()异步函数将返回博客文章。它使用GhostContentAPI中的posts.browse()方法,并设置include和limit参数。你已将include的值设置为”tags”,以获取标签和内容。limit的值设置为”all”,以获取所有的博客文章。如果发生错误,会在浏览器控制台上记录日志。
此时,api/ghost.js文件中包含以下代码。ghost.js文件位于utils文件夹中。
这是文章《如何使用Ghost和Next.js在Silicon Cloud上搭建您的博客》的第7部分(共17部分)。
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 文件。在 Head 导入语句上方的位置,添加下面突出显示的行来导入 getPosts 函数。
页面/index.js
import { getPosts } from './utils/ghost';
import Head from 'next/head';
…
现在你将创建一个异步函数(getStaticProps()),它将允许 Next.js 在构建时预渲染页面。当你想要利用静态生成时,这个函数非常有用。
在Home组件之后,创建异步函数getStaticProps(),并在函数体内调用getPosts()方法。从getPosts()方法中返回props的值。代码应如下所示:
index.js的页面
...
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进行编辑。
- nano pages/index.js
请对index.js进行以下突出显示的更改。
页面/index.js
export default function Home({posts}) {
return (
<div>
<Head>
<title>我的第一个博客</title>
<meta
name="description"
content="使用Next.js和Ghost创建的个人博客"
/>
</Head>
<main className="container mx-auto py-10">
<h1 className="text-center text-3xl">我的个人博客</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`组件。然后你会使用一个函数来获取博客文章的标题,替换了包含用于模拟的静态内容的`
- `标签。
您可以使用数组的.map()方法来迭代从props中获取的帖子集合,并获得每篇帖子的标题。最后,您返回一个包含帖子标题的<li>标签。
保存并关闭文件。如果使用Yarn,请使用npm run dev或yarn dev重新启动服务器。
返回到localhost:3000,现在你的博客将从Ghost渲染文章列表。
您的index.js文件的代码将与以下内容相匹配:
pages/index.js的内容可以用以下方式进行释义:
import { getPosts } from './utils/ghost';
import Head from 'next/head';
export default function Home({ posts }) {
return (
<div>
<Head>
<title>我的第一个博客</title>
<meta
name="description"
content="使用Next.js和Ghost创建的个人博客"
/>
</Head>
<main className="container mx-auto py-10">
<h1 className="text-center text-3xl">我的个人博客</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中检索并显示了帖子标题。然而,它仍然无法显示每个帖子的内容。在下一个部分中,您将创建动态路由并呈现帖子的内容。
第四步 – 渲染每个单独的帖子
在这一步中,您将编写代码从Ghost获取每篇博客文章的内容,创建动态路由,并在主页上将文章标题作为链接添加上去。
在Next.js中,您可以创建动态路由,以允许使用相同布局渲染页面。动态路由通过重用组件来减少代码的冗余。一旦创建了动态路由,所有的帖子都将使用同一个文件进行渲染。您无需为每篇帖子创建一个页面。
要创建动态路由并渲染单独的帖子,您需要:
- 编写一个功能来获取博客文章的内容。
- 创建动态路由。
- 将博客链接添加到物品列表中。
在ghost.js文件中,您编写了函数getPosts()来获取所有博客文章的列表。现在您将编写一个函数getSinglePost(),根据一个唯一标识获取您的帖子内容。
Ghost在使用标题时会自动为您的文章生成slug。例如,如果您的文章标题是”我的第一篇文章”,Ghost将生成my-first-post作为slug。这个slug有助于识别文章,并可以添加到您的域名URL上以显示内容。
getSinglePost()函数将以postSlug作为参数,并返回相应博客文章的内容。
如果服务器仍在运行,请停止服务器,然后打开pages/utils/ghost.js进行编辑。
在您的ghost.js文件的getPosts()函数下方,添加并导出一个异步函数getSinglePost()。
utils/ghost.js –> 实用工具/ghost.js
...
export async function getSinglePost(postSlug) {
return await api.posts
.read({
slug: postSlug
})
.catch(err => {
console.error(err);
});
}
getSinglePost() 函数使用 GhostContentAPI 的 posts.read() 方法,并将 postSlug 参数传递给 posts.read() 方法。
最后的/utils/ghost.js文件包含以下代码:
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);
});
}
以上代码用于连接Ghost CMS并获取文章数据。首先,我们导入GhostContentAPI模块并创建一个新的API实例,需要提供您的Ghost博客URL和API密钥。然后,我们定义两个异步函数:getPosts()用于获取所有文章及其标签,getSinglePost(postSlug)用于根据文章别名单独获取一篇文章。请记得将代码中的YOUR_URL替换为您的Ghost博客URL,将YOUR_API_KEY替换为您的Ghost API密钥。完成后保存并关闭文件。
在Next.js中,你可以在文件名中添加括号([param])来创建动态路由。例如,/post/[slug].js会创建动态路由。
在页面目录中创建一个名为 /post/[slug].js 的新文件。
nano pages/post/\[slug\].js
注意:Nano命令显示的括号是用反斜杠(\)转义的,bash需要这样才能创建带有括号的文件。
在路径/post/之后,文件名[slug].js将与任何字符字符串匹配。Next.js将在构建时为所有帖子创建页面。
在您的/post/[slug].js文件中,从../utils/ghost.js文件中导入getPosts()和getSinglePost()函数。
/post/[slug].js文件还包含了与帖子相关的模板。将以下代码添加到/post/[slug].js文件中:
页面/帖子/[别名].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() 函数创建一个功能组件。在该函数中,您从 props 中提取帖子对象,并从帖子对象中获取标题、html 和 feature_image。该组件返回用于创建页面的 HTML。
由于Ghost API将博客内容以HTML格式返回,因此<article>标签包含了从帖子对象提取的HTML值的dangerouslySetInnerHTML属性。dangerouslySetInnerHTML属性是React在浏览器DOM中使用innerHTML的替代方法。如果HTML来自不受信任的源头,您应该在将其传递给dangerouslySetInnerHTML属性之前对HTML进行净化处理。
getStaticProps()异步函数获取与slug(URL标识符)对应的博客文章内容。
为了将内容与URL正确映射,Next.js需要知道路径(在这种情况下是slug)的值。您可以使用getStaticPaths()函数来实现这一点。类似于getStaticProps()函数,编写一个名为getStaticPaths()的异步函数来返回一个slug列表。在[slug].js文件的末尾添加以下函数。
页面/帖子/[别名].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文件的内容修改为如下所示:
网页/帖子/[别名].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,
};
};
保存并关闭文件。如果使用Yarn,则使用npm run dev或yarn dev重启服务器。
在浏览器中打开localhost:3000/post/SLUG,将SLUG替换为与您的博客文章对应的短链接。现在您可以访问在浏览器中呈现的对应于该SLUG的博客内容。
目前,您必须手动输入URL才能访问帖子,这很不方便。您可以通过将超链接添加到主页上的项目列表中,以便从主页导航到帖子来解决这个问题。
如果服务器仍在运行,请停止它,并重新打开pages/index.js进行编辑。
请按照以下方式修改index.js中的突出部分,添加一个引入和一个链接组件:
页面/index.js
import { getPosts } from './utils/ghost';
import Head from 'next/head';
import Link from 'next/link';
export default function Home(props) {
return (
<div>
<Head>
<title>我的第一个博客</title
<meta
name="description"
content="使用Next.js和Ghost创建的个人博客"
/>
</Head>
<main className="container mx-auto py-10">
<h1 className="text-center text-3xl">我的个人博客</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 提供了一个链接组件,它允许客户端跳转。在你的 index.js 文件中,你从 next/link 中引入链接组件。在 <li> 标签中,你将帖子标题嵌套在链接组件中。和 HTML 中的锚点标签一样,链接标签也需要一个 href 属性。你将 slug 作为 href 属性的值传递进去。
保存文件并通过运行命令 npm run dev 或 yarn dev 重启开发服务器。
在浏览器中导航到 localhost:3000。现在,如果您点击任何帖子标题,您将导航到相应的博客文章。您的博客页面将显示如下:
您在第一步提供的标题和正文内容将显示在您的发布页面上。此示例的标题为”如何在Silicon Cloud上构建和部署您的博客”,内容摘自本教程的介绍部分。
在这个步骤中,您添加了从 Ghost CMS 检索单个帖子内容的功能,将它们渲染为单独的页面,并从索引页面链接到每个帖子。您现在已经创建了一个完整的博客,可以从 Ghost 中获取文章。
结论
在本文中,您在Silicon Cloud Droplet上部署了Ghost作为内容管理系统。您使用Next.js创建了自己的静态生成站点,并将Ghost CMS连接到Next.js网站,以提供内容。由于这是一个静态生成的网站,在客户端上发送的JavaScript较少,从而提高性能和SEO。您还使用TailwindCSS对博客进行了自定义设计。
目前,您的博客只在您的机器上运行。作为下一步,您可以在Silicon Cloud应用平台上部署它,以便其他人可以查看。要开始,请按照《部署Next.js应用到App平台》教程进行操作。
您还可以给您的博客添加更多功能。例如,您可以使用Algolia实现搜索功能,使用n8n自动化发布文章的流程,或者添加按标签列出文章的功能。
- 标签都包括className属性。这些属性包含了 Tailwind的样式类。
- yarn dev
- npm run dev
- yarn add @tryghost/content-api
- npm i @tryghost/content-api
- mkdir utils
- nano pages/utils/ghost.js
- 导航到YOUR_DOMAIN/ghost(YOUR_DOMAIN是您配置的网址),然后使用管理员凭据登录。
- 点击左侧边栏底部的齿轮图标以访问设置页面。
- 在高级类别中,点击左侧边栏上的集成。集成页面将显示可能的集成列表和自定义集成。
- 点击+ 添加自定义集成。将弹出一个窗口询问您要为集成命名。
- 在名称字段中输入集成的名称,然后点击创建。这将带您到配置自定义集成的页面。
- 复制显示的内容API密钥(不是管理员API密钥)。
- 点击保存以保存自定义集成。
- nano pages/index.js
- `标签。
您可以使用数组的.map()方法来迭代从props中获取的帖子集合,并获得每篇帖子的标题。最后,您返回一个包含帖子标题的<li>标签。
保存并关闭文件。如果使用Yarn,请使用npm run dev或yarn dev重新启动服务器。
返回到localhost:3000,现在你的博客将从Ghost渲染文章列表。
您的index.js文件的代码将与以下内容相匹配:
pages/index.js的内容可以用以下方式进行释义:import { getPosts } from './utils/ghost'; import Head from 'next/head'; export default function Home({ posts }) { return ( <div> <Head> <title>我的第一个博客</title> <meta name="description" content="使用Next.js和Ghost创建的个人博客" /> </Head> <main className="container mx-auto py-10"> <h1 className="text-center text-3xl">我的个人博客</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中检索并显示了帖子标题。然而,它仍然无法显示每个帖子的内容。在下一个部分中,您将创建动态路由并呈现帖子的内容。
第四步 – 渲染每个单独的帖子
在这一步中,您将编写代码从Ghost获取每篇博客文章的内容,创建动态路由,并在主页上将文章标题作为链接添加上去。
在Next.js中,您可以创建动态路由,以允许使用相同布局渲染页面。动态路由通过重用组件来减少代码的冗余。一旦创建了动态路由,所有的帖子都将使用同一个文件进行渲染。您无需为每篇帖子创建一个页面。
要创建动态路由并渲染单独的帖子,您需要:
- 编写一个功能来获取博客文章的内容。
- 创建动态路由。
- 将博客链接添加到物品列表中。
在ghost.js文件中,您编写了函数getPosts()来获取所有博客文章的列表。现在您将编写一个函数getSinglePost(),根据一个唯一标识获取您的帖子内容。
Ghost在使用标题时会自动为您的文章生成slug。例如,如果您的文章标题是”我的第一篇文章”,Ghost将生成my-first-post作为slug。这个slug有助于识别文章,并可以添加到您的域名URL上以显示内容。
getSinglePost()函数将以postSlug作为参数,并返回相应博客文章的内容。
如果服务器仍在运行,请停止服务器,然后打开pages/utils/ghost.js进行编辑。
在您的ghost.js文件的getPosts()函数下方,添加并导出一个异步函数getSinglePost()。
utils/ghost.js –> 实用工具/ghost.js... export async function getSinglePost(postSlug) { return await api.posts .read({ slug: postSlug }) .catch(err => { console.error(err); }); }
getSinglePost() 函数使用 GhostContentAPI 的 posts.read() 方法,并将 postSlug 参数传递给 posts.read() 方法。
最后的/utils/ghost.js文件包含以下代码:
utils/ghost.jsimport 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); }); }
以上代码用于连接Ghost CMS并获取文章数据。首先,我们导入GhostContentAPI模块并创建一个新的API实例,需要提供您的Ghost博客URL和API密钥。然后,我们定义两个异步函数:getPosts()用于获取所有文章及其标签,getSinglePost(postSlug)用于根据文章别名单独获取一篇文章。请记得将代码中的YOUR_URL替换为您的Ghost博客URL,将YOUR_API_KEY替换为您的Ghost API密钥。完成后保存并关闭文件。
在Next.js中,你可以在文件名中添加括号([param])来创建动态路由。例如,/post/[slug].js会创建动态路由。
在页面目录中创建一个名为 /post/[slug].js 的新文件。
nano pages/post/\[slug\].js
注意:Nano命令显示的括号是用反斜杠(\)转义的,bash需要这样才能创建带有括号的文件。
在路径/post/之后,文件名[slug].js将与任何字符字符串匹配。Next.js将在构建时为所有帖子创建页面。
在您的/post/[slug].js文件中,从../utils/ghost.js文件中导入getPosts()和getSinglePost()函数。
/post/[slug].js文件还包含了与帖子相关的模板。将以下代码添加到/post/[slug].js文件中:
页面/帖子/[别名].jsimport { 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() 函数创建一个功能组件。在该函数中,您从 props 中提取帖子对象,并从帖子对象中获取标题、html 和 feature_image。该组件返回用于创建页面的 HTML。
由于Ghost API将博客内容以HTML格式返回,因此<article>标签包含了从帖子对象提取的HTML值的dangerouslySetInnerHTML属性。dangerouslySetInnerHTML属性是React在浏览器DOM中使用innerHTML的替代方法。如果HTML来自不受信任的源头,您应该在将其传递给dangerouslySetInnerHTML属性之前对HTML进行净化处理。
getStaticProps()异步函数获取与slug(URL标识符)对应的博客文章内容。
为了将内容与URL正确映射,Next.js需要知道路径(在这种情况下是slug)的值。您可以使用getStaticPaths()函数来实现这一点。类似于getStaticProps()函数,编写一个名为getStaticPaths()的异步函数来返回一个slug列表。在[slug].js文件的末尾添加以下函数。
页面/帖子/[别名].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文件的内容修改为如下所示:
网页/帖子/[别名].jsimport { 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, }; };
保存并关闭文件。如果使用Yarn,则使用npm run dev或yarn dev重启服务器。
在浏览器中打开localhost:3000/post/SLUG,将SLUG替换为与您的博客文章对应的短链接。现在您可以访问在浏览器中呈现的对应于该SLUG的博客内容。
目前,您必须手动输入URL才能访问帖子,这很不方便。您可以通过将超链接添加到主页上的项目列表中,以便从主页导航到帖子来解决这个问题。
如果服务器仍在运行,请停止它,并重新打开pages/index.js进行编辑。
请按照以下方式修改index.js中的突出部分,添加一个引入和一个链接组件:
页面/index.js
import { getPosts } from './utils/ghost'; import Head from 'next/head'; import Link from 'next/link'; export default function Home(props) { return ( <div> <Head> <title>我的第一个博客</title <meta name="description" content="使用Next.js和Ghost创建的个人博客" /> </Head> <main className="container mx-auto py-10"> <h1 className="text-center text-3xl">我的个人博客</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 提供了一个链接组件,它允许客户端跳转。在你的 index.js 文件中,你从 next/link 中引入链接组件。在 <li> 标签中,你将帖子标题嵌套在链接组件中。和 HTML 中的锚点标签一样,链接标签也需要一个 href 属性。你将 slug 作为 href 属性的值传递进去。
保存文件并通过运行命令 npm run dev 或 yarn dev 重启开发服务器。
在浏览器中导航到 localhost:3000。现在,如果您点击任何帖子标题,您将导航到相应的博客文章。您的博客页面将显示如下:
您在第一步提供的标题和正文内容将显示在您的发布页面上。此示例的标题为”如何在Silicon Cloud上构建和部署您的博客”,内容摘自本教程的介绍部分。
在这个步骤中,您添加了从 Ghost CMS 检索单个帖子内容的功能,将它们渲染为单独的页面,并从索引页面链接到每个帖子。您现在已经创建了一个完整的博客,可以从 Ghost 中获取文章。
结论
在本文中,您在Silicon Cloud Droplet上部署了Ghost作为内容管理系统。您使用Next.js创建了自己的静态生成站点,并将Ghost CMS连接到Next.js网站,以提供内容。由于这是一个静态生成的网站,在客户端上发送的JavaScript较少,从而提高性能和SEO。您还使用TailwindCSS对博客进行了自定义设计。
目前,您的博客只在您的机器上运行。作为下一步,您可以在Silicon Cloud应用平台上部署它,以便其他人可以查看。要开始,请按照《部署Next.js应用到App平台》教程进行操作。
您还可以给您的博客添加更多功能。例如,您可以使用Algolia实现搜索功能,使用n8n自动化发布文章的流程,或者添加按标签列出文章的功能。
现在,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 Silicon Cloud</li>
<li>How to style a Next.js website</li>
<li>How to cross-post your articles automatically</li>
</ul>
</div>
</main>
</div>
);
}
保存并关闭文件。
在本地启动Web服务器。如果您使用的是Yarn,请执行以下命令:
如果你正在使用npm,请运行以下命令:
在浏览器中导航到https://localhost:3000,你会发现一个从模拟数据中获取的博客文章列表。你的首页将以类似以下的方式显示:

在这一步中,您创建了博客的主页并添加了一系列模拟文章。在下一步中,您将从Ghost中拉取博客文章。
第三步 – 从Ghost获取所有博客文章
在这一步骤中,你将从Ghost中获取你创建的博客文章,并在浏览器上呈现它们。
要从Ghost获取您的文章,您首先必须安装Ghost内容API的JavaScript库。使用键盘快捷键CTRL+C停止服务器。在终端中运行以下命令安装该库:
如果你正在使用Yarn,请运行以下命令:
如果你正在使用npm,运行以下命令:
在成功安装了该库之后,你现在将创建一个文件来存储获取博客文章的逻辑。在pages目录下,创建一个名为utils的新文件夹。
在该文件夹内创建一个名为ghost.js的新文件。
在ghost.js文件中,从Ghost Content API库中导入GhostContentAPI模块。初始化一个新的GhostContentAPI对象并将其值存储在常量变量api中。您需要传递主机、API密钥和版本的值。代码如下所示:
utils/ghost.js
的中文解释
import GhostContentAPI from "@tryghost/content-api";
const api = new GhostContentAPI({
url: `YOUR_URL`,
key: `YOUR_API_KEY`,
version: 'v5.0'
});
YOUR_URL的值是您在安装Ghost时配置的域名,包括协议,很可能是https://。
要找到您的Ghost API密钥,请按照以下步骤操作:
在 ghost.js 文件中,将 YOUR_API_KEY 替换为复制的 API 密钥。
现在,您已经初始化了GhostContentAPI,您将编写一个异步函数来从您的Ghost安装中获取所有文章。该函数会获取博客文章而不考虑它们的标签。请将以下代码复制并粘贴到您的ghost.js文件中:
功能/ghost.js
...
export async function getPosts() {
return await api.posts
.browse({
include:"tags",
limit: "all"
})
.catch(err => {
console.error(err);
});
}
承诺得到解决后,getPosts()异步函数将返回博客文章。它使用GhostContentAPI中的posts.browse()方法,并设置include和limit参数。你已将include的值设置为”tags”,以获取标签和内容。limit的值设置为”all”,以获取所有的博客文章。如果发生错误,会在浏览器控制台上记录日志。
此时,api/ghost.js文件中包含以下代码。ghost.js文件位于utils文件夹中。
这是文章《如何使用Ghost和Next.js在Silicon Cloud上搭建您的博客》的第7部分(共17部分)。
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 文件。在 Head 导入语句上方的位置,添加下面突出显示的行来导入 getPosts 函数。
import { getPosts } from './utils/ghost';
import Head from 'next/head';
…
现在你将创建一个异步函数(getStaticProps()),它将允许 Next.js 在构建时预渲染页面。当你想要利用静态生成时,这个函数非常有用。
在Home组件之后,创建异步函数getStaticProps(),并在函数体内调用getPosts()方法。从getPosts()方法中返回props的值。代码应如下所示:
...
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进行编辑。
请对index.js进行以下突出显示的更改。
export default function Home({posts}) {
return (
<div>
<Head>
<title>我的第一个博客</title>
<meta
name="description"
content="使用Next.js和Ghost创建的个人博客"
/>
</Head>
<main className="container mx-auto py-10">
<h1 className="text-center text-3xl">我的个人博客</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`组件。然后你会使用一个函数来获取博客文章的标题,替换了包含用于模拟的静态内容的`