如何使用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进行部署。

先决条件

完成本教程,您需要以下物品:

  • A Silicon Cloud account. If you do not have one, sign up for a new account.
  • Ghost installed on an Ubuntu server with at least 1 GB of memory. You can deploy a Ghost One-Click Droplet on Silicon Cloud or follow the Ghost documentation for manual setup.
  • A registered domain name to point to your Ghost blog. You can find instructions on setting your domain on Silicon Cloud nameserver in the How To Point to Silicon Cloud Nameservers From Common Domain Registrars guide.
  • A local development environment for Node.js. Follow How to Install Node.js and Create a Local Development Environment.
  • A Next.js project on your local machine. You can create a Next.js project through the library’s Setup step in the Getting Started guide. Use create-next-app and accept the configuration defaults.

Note

注意:你可以使用TypeScript或JavaScript创建Next.js项目。本教程使用JavaScript,但两种语言都可以使用。
  • Tailwind installed on your local machine and configured for Next.js. After installing Next.js, follow the instructions in the Install Tailwind CSS with Next.js guide.
  • A text editor like Visual Studio Code. This tutorial uses nano.
  • Familiarity with React, which you can gain through the How To Code in React.js series.

步骤1 — 在Ghost上发布帖子

在这一步中,您将在Ghost上创建一个帐户并发布一些示例文章,这样将来您在后续步骤中便有内容可呈现。

安装并设置了Ghost后,您将需要一个管理员帐户来管理您的博客。导航到YOUR_DOMAIN/ghost,其中YOUR_DOMAIN是您在Ghost安装过程中输入的URL。

你会看到一个“欢迎来到Ghost”的页面,在那里你将被邀请创建一个账户。输入站点的标题、你的姓名、电子邮箱地址和密码。然后点击“创建账户并开始发布”按钮。

在左侧边栏中,在“您首先想要做什么?”下面,选择“撰写您的第一篇帖子”选项。输入博客文章的标题和内容,并点击右上角的“发布”。接着点击“继续”,最终审阅后点击“立即发布帖子”。

导航到YOUR_DOMAIN/ghost以查看您的仪表板。在左侧边栏中,选择“查看站点”以查看您网站的实时预览。

Ghost Dashboard

在本教程的后面,您将在您的网站上发布几篇博客文章。从仪表板中,选择左侧边栏的”帖子”以创建两篇更多的博客文章。给它们起独特的名称,以便您之后能够辨认它们。

在这一步中,您将在Ghost上设置您的管理页面并发布博客文章。您的博客目前正在使用Ghost提供的默认模板。在下一步中,您将创建一个Next.js项目,这将为您提供更多的设计灵活性。您将使用Ghost作为内容管理系统(CMS),使用Next.js来构建前端。

第二步 – 创建一个Next.js项目

现在你已经准备好为你的博客使用Ghost了。在这一步中,你将使用Next.js为你的博客创建前端,并使用Tailwind CSS为其添加样式。

在你偏好的代码编辑器中打开你创建的Next.js项目。打开pages/index.js文件。

  1. nano pages/index.js

在文件中,删除现有的代码,然后添加以下内容来从next/head导入Head组件并创建一个React组件。

页面/index.js
import Head from 'next/head';

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

Head 组件允许您在 HTML 中添加诸如 和 <meta> 这样的标记。</p> <p>在 Head 组件的基础上,添加一个包含 </p> <div> 标签的 return 语句。在 </p> <div> 标签内,加入一个 <Head> 标签,其包含一个 <title> 标签和一个 <meta> 标签。</p> <div>pages/index.js 的内容进行改写。</div> <pre class="post-pre"><code><span class="token spread operator">...</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span><span class="token plain-text">My First Blog</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>My personal blog created with Next.js and Ghost<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token spread operator">...</span> </code></pre> <p><title>标签将您的网页标题设置为“我的第一个博客”。<meta>标签的name属性设置为“description”,content属性设置为“使用Next.js和Ghost创建的个人博客”。这个标题和描述将在您网站的预览中显示。</p> <p>现在您已经设置了标题和描述,您将显示一个文章列表。这段代码片段暂时使用虚拟数据。在<Head>标签下面,使用</p> <li>标签添加一系列文章:</p> <div>index.js文件中的页面</div> <pre class="post-pre"><code><span class="token spread operator">...</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> ... </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container mx-auto py-10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></mark><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-center text-3xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">My Personal Blog</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span></span></mark><span class="token tag"><span class="token punctuation">></span></span><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flex justify-center mt-10 <span class="token punctuation">"</span></span><span class="token punctuation">></span></span></mark><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></mark><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token plain-text">How to build and deploy your blog on Silicon Cloud</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span></span></mark><span class="token tag"><span class="token punctuation">></span></span><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token plain-text">How to style a Next.js website</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span></span></mark><span class="token tag"><span class="token punctuation">></span></span><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token plain-text">How to cross-post your articles automatically</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span></span></mark><span class="token tag"><span class="token punctuation">></span></span><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span></mark><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></mark><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></mark><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token spread operator">...</span> </code></pre> <p>你现在已经添加了一个包含标题和</p> <div>标签的<main>标签。 </p> <div>标签包含一个包含几个虚拟文章标题的无序列表。 <main>、</p> <h1>、</p> <div>和</p> <ul> 标签都包括className属性。这些属性包含了 Tailwind的样式类。</p> <p>现在,index.js文件应该如下所示:</p> <div>页面 / 首页 / index.js</div> <pre class="post-pre"><code><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Head</span></span> <span class="token keyword module">from</span> <span class="token string">'next/head'</span><span class="token punctuation">;</span> <span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">Home</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span><span class="token plain-text">My First Blogp</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>My personal blog created with Next.js and Ghost<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container mx-auto py-10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-center text-3xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">My Personal Blog</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flex justify-center mt-10 <span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token plain-text">How to build and deploy your blog on Silicon Cloud</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token plain-text">How to style a Next.js website</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span><span class="token punctuation">></span></span><span class="token plain-text">How to cross-post your articles automatically</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>保存并关闭文件。</p> <p>在本地启动Web服务器。如果您使用的是Yarn,请执行以下命令:</p> <pre class="post-pre"><code><ol><li data-prefix="$"><span class="token function">yarn</span> dev </li></ol> </code></pre> <p>如果你正在使用npm,请运行以下命令:</p> <pre class="post-pre"><code><ol><li data-prefix="$"><span class="token function">npm</span> run dev </li></ol> </code></pre> <p>在浏览器中导航到https://localhost:3000,你会发现一个从模拟数据中获取的博客文章列表。你的首页将以类似以下的方式显示:</p> <div> <img decoding="async" src="https://cdn.silicloud.com/blog-img/blog/img/65644c5aa4b2f92e6c71e017/44-0.png" class='post-images' alt="Home page listing the mock data" title=""> </div> <p>在这一步中,您创建了博客的主页并添加了一系列模拟文章。在下一步中,您将从Ghost中拉取博客文章。</p> <h2>第三步 – 从Ghost获取所有博客文章.</h2> <p>在这一步骤中,你将从Ghost中获取你创建的博客文章,并在浏览器上呈现它们。</p> <p>要从Ghost获取您的文章,您首先必须安装Ghost内容API的JavaScript库。使用键盘快捷键CTRL+C停止服务器。在终端中运行以下命令安装该库:</p> <p>如果你正在使用Yarn,请运行以下命令:</p> <pre class="post-pre"><code><ol><li data-prefix="$"><span class="token function">yarn</span> <span class="token function">add</span> @tryghost/content-api </li></ol> </code></pre> <p>如果你正在使用npm,运行以下命令:</p> <pre class="post-pre"><code><ol><li data-prefix="$"><span class="token function">npm</span> i @tryghost/content-api </li></ol> </code></pre> <p>在成功安装了该库之后,你现在将创建一个文件来存储获取博客文章的逻辑。在pages目录下,创建一个名为utils的新文件夹。</p> <pre class="post-pre"><code><ol><li data-prefix="$"><span class="token function">mkdir</span> utils </li></ol> </code></pre> <p>在该文件夹内创建一个名为ghost.js的新文件。</p> <pre class="post-pre"><code><ol><li data-prefix="$"><span class="token function">nano</span> pages/utils/ghost.js </li></ol> </code></pre> <p>在ghost.js文件中,从Ghost Content API库中导入GhostContentAPI模块。初始化一个新的GhostContentAPI对象并将其值存储在常量变量api中。您需要传递主机、API密钥和版本的值。代码如下所示:</p> <div> `utils/ghost.js` 的中文解释</div> <pre class="post-pre"><code><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">GhostContentAPI</span></span> <span class="token keyword module">from</span> <span class="token string">"@tryghost/content-api"</span><span class="token punctuation">;</span> <span class="token keyword">const</span> api <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GhostContentAPI</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><mark><span class="token string">YOUR_URL</span></mark><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><mark><span class="token string">YOUR_API_KEY</span></mark><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token literal-property property">version</span><span class="token operator">:</span> <span class="token string">'<mark>v5.0</mark>'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> </code></pre> <p>YOUR_URL的值是您在安装Ghost时配置的域名,包括协议,很可能是https://。</p> <p>要找到您的Ghost API密钥,请按照以下步骤操作:</p> <ol>导航到YOUR_DOMAIN/ghost(YOUR_DOMAIN是您配置的网址),然后使用管理员凭据登录。<br /> 点击左侧边栏底部的齿轮图标以访问设置页面。<br /> 在高级类别中,点击左侧边栏上的集成。集成页面将显示可能的集成列表和自定义集成。<br /> 点击+ 添加自定义集成。将弹出一个窗口询问您要为集成命名。<br /> 在名称字段中输入集成的名称,然后点击创建。这将带您到配置自定义集成的页面。<br /> 复制显示的内容API密钥(不是管理员API密钥)。<br /> 点击保存以保存自定义集成。</ol> <p>在 ghost.js 文件中,将 YOUR_API_KEY 替换为复制的 API 密钥。</p> <p>现在,您已经初始化了GhostContentAPI,您将编写一个异步函数来从您的Ghost安装中获取所有文章。该函数会获取博客文章而不考虑它们的标签。请将以下代码复制并粘贴到您的ghost.js文件中:</p> <div>功能/ghost.js</div> <pre class="post-pre"><code><span class="token spread operator">...</span> <span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getPosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token keyword control-flow">await</span> api<span class="token punctuation">.</span><span class="token property-access">posts</span> <span class="token punctuation">.</span><span class="token method function property-access">browse</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">include</span><span class="token operator">:</span><span class="token string">"tags"</span><span class="token punctuation">,</span> <span class="token literal-property property">limit</span><span class="token operator">:</span> <span class="token string">"all"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token parameter">err</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>承诺得到解决后,getPosts()异步函数将返回博客文章。它使用GhostContentAPI中的posts.browse()方法,并设置包括和限制参数。你已将include的值设置为tags,以获取标签和内容。限制的值设置为all,以获取所有的博客文章。如果发生错误,会在浏览器控制台上记录日志。</p> <p>此时,api/ghost.js文件中包含以下代码。</p> <div>ghost.js文件位于utils文件夹中。</div> <pre class="post-pre"><code><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">GhostContentAPI</span></span> <span class="token keyword module">from</span> <span class="token string">'@tryghost/content-api'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> api <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GhostContentAPI</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><mark><span class="token string">YOUR_URL</span></mark><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><mark><span class="token string">YOUR_API_KEY</span></mark><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token literal-property property">version</span><span class="token operator">:</span> <span class="token string">'v5.0'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getPosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token keyword control-flow">await</span> api<span class="token punctuation">.</span><span class="token property-access">posts</span> <span class="token punctuation">.</span><span class="token method function property-access">browse</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">include</span><span class="token operator">:</span> <span class="token string">'tags'</span><span class="token punctuation">,</span> <span class="token literal-property property">limit</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>保存并关闭文件。</p> <p>要显示帖子列表,请打开你的 index.js 文件。在 Head 导入语句上方的位置,添加下面突出显示的行来导入 getPosts 函数。</p> <div>页面/index.js</div> <pre class="post-pre"><code><mark><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> getPosts <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">'./utils/ghost'</span><span class="token punctuation">;</span></mark> <span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Head</span></span> <span class="token keyword module">from</span> <span class="token string">'next/head'</span><span class="token punctuation">;</span> … </code></pre> <p>现在你将创建一个异步函数(getStaticProps()),它将允许 Next.js 在构建时预渲染页面。当你想要利用静态生成时,这个函数非常有用。</p> <p>在Home组件之后,创建异步函数getStaticProps(),并在函数体内调用getPosts()方法。从getPosts()方法中返回props的值。代码应如下所示:</p> <div>index.js的页面</div> <pre class="post-pre"><code><span class="token spread operator">...</span> <span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getStaticProps</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> posts <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">getPosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> posts <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>保存文件。</p> <p>现在已经定义了getStaticProps()方法,请使用npm run dev或者yarn dev(如果使用Yarn)来重新启动服务器。</p> <p>在你的浏览器中,页面上仍然显示的是静态数据。该页面没有显示你从Ghost获取的数据。这是因为你是在获取值,但没有将其呈现出来。</p> <p>你需要对index.js进行一些更改,以便你的Home组件可以使用从Ghost检索到的值。</p> <p>按下CTRL-C停止服务器,然后打开index.js进行编辑。</p> <pre class="post-pre"><code><ol><li data-prefix="$"><span class="token function">nano</span> pages/index.js </li></ol> </code></pre> <p>请对index.js进行以下突出显示的更改。</p> <div>页面/index.js</div> <pre class="post-pre"><code><span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">Home</span></span><span class="token punctuation">(</span><mark><span class="token parameter"><span class="token punctuation">{</span>posts<span class="token punctuation">}</span></span></mark><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span><span class="token plain-text">My First Blog</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>My personal blog created with Next.js and Ghost<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container mx-auto py-10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-center text-3xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">My Personal Blog</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flex justify-center mt-10 <span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><mark><span class="token punctuation">{</span>posts<span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">(</span></mark> <mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>post<span class="token punctuation">.</span><span class="token property-access">title</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>post<span class="token punctuation">.</span><span class="token property-access">title</span><span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span></span></mark><span class="token tag"><span class="token punctuation">></span></span> <mark><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span></mark><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>在这段代码中,首先你会解构并将`posts`作为参数传递给`Home`组件。然后你会使用一个函数来获取博客文章的标题,替换了包含用于模拟的静态内容的`</p> <li>`标签。</p> <p>您可以使用数组的.map()方法来迭代从props中获取的帖子集合,并获得每篇帖子的标题。最后,您返回一个包含帖子标题的</p> <li>标签。</p> <p>保存并关闭文件。如果使用Yarn,请使用npm run dev或yarn dev重新启动服务器。</p> <p>返回到localhost:3000,现在你的博客从Ghost渲染文章列表。</p> <p>你的index.js文件的代码将与以下内容相匹配:</p> <div>pages/index.js的内容可以用以下方式进行释义:</div> <pre class="post-pre"><code><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> getPosts <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">'./utils/ghost'</span><span class="token punctuation">;</span> <span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Head</span></span> <span class="token keyword module">from</span> <span class="token string">'next/head'</span><span class="token punctuation">;</span> <span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">Home</span></span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> posts <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span><span class="token plain-text">My First Blog</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>My personal blog created with Next.js and Ghost<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container mx-auto py-10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-center text-3xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">My Personal Blog</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flex justify-center mt-10 <span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token punctuation">{</span>posts<span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>post<span class="token punctuation">.</span><span class="token property-access">title</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>post<span class="token punctuation">.</span><span class="token property-access">title</span><span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getStaticProps</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> posts <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">getPosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> posts <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>您的首页将呈现类似于以下的样式:</p> <div> <img decoding="async" src="https://cdn.silicloud.com/blog-img/blog/img/65644c5aa4b2f92e6c71e017/96-0.png" class='post-images' alt="Home page listing the articles from Ghost" title=""> </div> <p>您的博客现已从CMS中检索并显示了帖子标题。然而,它仍然无法显示每个帖子的内容。在下一个部分中,您将创建动态路由并呈现帖子的内容。</p> <h2>第四步 – 渲染每个单独的帖子</h2> <p>在这一步中,您将编写代码从Ghost获取每篇博客文章的内容,创建动态路由,并在主页上将文章标题作为链接添加上去。</p> <p>在Next.js中,您可以创建动态路由,以允许使用相同布局渲染页面。动态路由通过重用组件来减少代码的冗余。一旦创建了动态路由,所有的帖子都将使用同一个文件进行渲染。您无需为每篇帖子创建一个页面。</p> <p>要创建动态路由并渲染单独的帖子,你需要:</p> <ol>编写一个功能来获取博客文章的内容。<br /> 创建动态路由。<br /> 将博客链接添加到物品列表中。</ol> <p>在ghost.js文件中,你编写了函数getPosts()来获取所有博客文章的列表。现在你将编写一个函数getSinglePost(),根据一个唯一标识获取你的帖子内容。</p> <p>Ghost在使用标题标题时会自动为您的文章生成slug。例如,如果您的文章标题是“我的第一篇文章”,Ghost将生成my-first-post作为slug。这个slug有助于识别文章,并可以添加到您的域名URL上以显示内容。</p> <p>getSinglePost()函数将以postSlug作为参数,并返回相应博客文章的内容。</p> <p>如果服务器仍在运行,请停止服务器,然后打开pages/utils/ghost.js进行编辑。</p> <p>在你的ghost.js文件的getPosts()函数下方,添加并导出一个异步函数getSinglePost()。</p> <div>utils/ghost.js –> 实用工具/幽灵.js</div> <pre class="post-pre"><code><span class="token spread operator">...</span> <span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getSinglePost</span><span class="token punctuation">(</span><span class="token parameter">postSlug</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token keyword control-flow">await</span> api<span class="token punctuation">.</span><span class="token property-access">posts</span> <span class="token punctuation">.</span><span class="token method function property-access">read</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">slug</span><span class="token operator">:</span> postSlug <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token parameter">err</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>getSinglePost() 函数使用 GhostContentAPI 的 posts.read() 方法,并将 postSlug 参数传递给 posts.read() 方法。</p> <p>最后的/utils/ghost.js文件包含以下代码:</p> <div>utils/ghost.js 工具/鬼.js</div> <pre class="post-pre"><code><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">GhostContentAPI</span></span> <span class="token keyword module">from</span> <span class="token string">'@tryghost/content-api'</span><span class="token punctuation">;</span> <span class="token keyword">const</span> api <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GhostContentAPI</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">url</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><mark><span class="token string">YOUR_URL</span></mark><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token literal-property property">key</span><span class="token operator">:</span> <span class="token template-string"><span class="token template-punctuation string">`</span><mark><span class="token string">YOUR_API_KEY</span></mark><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token literal-property property">version</span><span class="token operator">:</span> <span class="token string">'v5.0'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getPosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token keyword control-flow">await</span> api<span class="token punctuation">.</span><span class="token property-access">posts</span> <span class="token punctuation">.</span><span class="token method function property-access">browse</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">include</span><span class="token operator">:</span> <span class="token string">'tags'</span><span class="token punctuation">,</span> <span class="token literal-property property">limit</span><span class="token operator">:</span> <span class="token string">'all'</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getSinglePost</span><span class="token punctuation">(</span><span class="token parameter">postSlug</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token keyword control-flow">await</span> api<span class="token punctuation">.</span><span class="token property-access">posts</span> <span class="token punctuation">.</span><span class="token method function property-access">read</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">slug</span><span class="token operator">:</span> postSlug <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token keyword control-flow">catch</span><span class="token punctuation">(</span><span class="token parameter">err</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token console class-name">console</span><span class="token punctuation">.</span><span class="token method function property-access">error</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>保存并关闭文件。</p> <p>在Next.js中,你可以在文件名中添加括号([param])来创建动态路由。例如,/post/[slug].js会创建动态路由。</p> <p>在页面目录中创建一个名为 /post/[slug].js 的新文件。</p> <pre class="post-pre"><code>nano pages/post/\[slug\].js </code></pre> <div class="post-conf-note"> <p class="post-note"> <p class="post-conf-desc">Note</p> <div>注意:Nano命令显示的括号是用反斜杠(\)转义的,bash需要这样才能创建带有括号的文件。</div> </div> <p>在路径/post/之后,文件名[slug].js将与任何字符字符串匹配。Next.js将在构建时为所有帖子创建页面。</p> <p>在您的/post/[slug].js文件中,从../utils/ghost.js文件中导入getPosts()和getSinglePost()函数。</p> <p>/post/[slug].js文件还包含了与帖子相关的模板。将以下代码添加到/post/[slug].js文件中:</p> <div>页面/帖子/[别名].js</div> <pre class="post-pre"><code><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> getPosts<span class="token punctuation">,</span> getSinglePost <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">'../utils/ghost'</span><span class="token punctuation">;</span> <span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">PostTemplate</span></span><span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> post <span class="token punctuation">}</span> <span class="token operator">=</span> props<span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> title<span class="token punctuation">,</span> html<span class="token punctuation">,</span> feature_image <span class="token punctuation">}</span> <span class="token operator">=</span> post<span class="token punctuation">;</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>main className<span class="token operator">=</span><span class="token string">"container mx-auto py-10"</span><span class="token operator">></span> <span class="token operator"><</span>h1 className<span class="token operator">=</span><span class="token string">"text-center text-3xl"</span><span class="token operator">></span><span class="token punctuation">{</span>title<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>article className<span class="token operator">=</span><span class="token string">"mt-10 leading-7 text-justify"</span> dangerouslySetInnerHTML<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token literal-property property">__html</span><span class="token operator">:</span> html <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>main<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getStaticProps</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> params <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> post <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">getSinglePost</span><span class="token punctuation">(</span>params<span class="token punctuation">.</span><span class="token property-access">slug</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> post <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> </code></pre> <p>PostTemplate() 函数创建一个功能组件。在该函数中,您从 props 中提取帖子对象,并从帖子对象中获取标题、html 和 feature_image。该组件返回用于创建页面的 HTML。</p> <p>由于Ghost API将博客内容以HTML格式返回,因此</p> <article>标签包含了从帖子对象提取的HTML值的dangerouslySetInnerHTML属性。dangerouslySetInnerHTML属性是React在浏览器DOM中使用innerHTML的替代方法。如果HTML来自不受信任的源头,您应该在将其传递给dangerouslySetInnerHTML属性之前对HTML进行净化处理。</p> <p>getStaticProps()异步函数获取与slug对应的博客文章内容。</p> <p>为了将内容与URL正确映射,Next.js需要知道路径(在这种情况下是slug)的值。您可以使用getStaticPaths()函数来实现这一点。类似于getStaticProps()函数,编写一个名为getStaticPaths()的异步函数来返回一个slug列表。在[slug].js文件的末尾添加以下函数。</p> <div>页面/帖子/[别名].js</div> <pre class="post-pre"><code><span class="token spread operator">...</span> <span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getStaticPaths</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> allPosts <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">getPosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">paths</span><span class="token operator">:</span> allPosts<span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> slug <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">params</span><span class="token operator">:</span> <span class="token punctuation">{</span> slug <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token literal-property property">fallback</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> </code></pre> <div class="post-conf-note"> <p class="post-note"> <p class="post-conf-desc">Note</p> <div>注意:通过将fallback的值设置为false,任何没有通过getStaticPaths()返回的路径都会导致404页面。</div> </div> <p>请将/post/[slug].js文件的内容修改为如下所示:</p> <div>网页/帖子/[别名].js</div> <pre class="post-pre"><code><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> getPosts<span class="token punctuation">,</span> getSinglePost <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">'../utils/ghost'</span><span class="token punctuation">;</span> <span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">PostTemplate</span></span><span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> post <span class="token punctuation">}</span> <span class="token operator">=</span> props<span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> title<span class="token punctuation">,</span> html<span class="token punctuation">,</span> feature_image <span class="token punctuation">}</span> <span class="token operator">=</span> post<span class="token punctuation">;</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span> <span class="token operator"><</span>main className<span class="token operator">=</span><span class="token string">"container mx-auto py-10"</span><span class="token operator">></span> <span class="token operator"><</span>h1 className<span class="token operator">=</span><span class="token string">"text-center text-3xl"</span><span class="token operator">></span><span class="token punctuation">{</span>title<span class="token punctuation">}</span><span class="token operator"><</span><span class="token operator">/</span>h1<span class="token operator">></span> <span class="token operator"><</span>article className<span class="token operator">=</span><span class="token string">"mt-10 leading-7 text-justify"</span> dangerouslySetInnerHTML<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> <span class="token literal-property property">__html</span><span class="token operator">:</span> html <span class="token punctuation">}</span><span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">></span> <span class="token operator"><</span><span class="token operator">/</span>main<span class="token operator">></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getStaticProps</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> params <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> post <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">getSinglePost</span><span class="token punctuation">(</span>params<span class="token punctuation">.</span><span class="token property-access">slug</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> post <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token keyword module">export</span> <span class="token keyword">const</span> <span class="token function-variable function">getStaticPaths</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> allPosts <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">getPosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">paths</span><span class="token operator">:</span> allPosts<span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> slug <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">params</span><span class="token operator">:</span> <span class="token punctuation">{</span> slug <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token literal-property property">fallback</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> </code></pre> <p>保存并关闭文件。如果使用Yarn,则使用npm run dev或yarn dev重启服务器。</p> <p>在浏览器中打开localhost:3000/post/SLUG,将SLUG替换为与您的博客文章对应的短链接。现在您可以访问在浏览器中呈现的对应于<^<SLUG<^>的博客内容。</p> <p>目前,您必须手动输入URL才能访问帖子,这很不方便。您可以通过将超链接添加到主页上的项目列表中,以便从主页导航到帖子来解决这个问题。</p> <p>如果服务器仍在运行,请停止它,并重新打开pages/index.js进行编辑。</p> <p>请按照以下方式修改index.js中的突出部分,添加一个引入和一个链接组件:</p> <div>页面/index.js</div> <pre class="post-pre"><code><span class="token keyword module">import</span> <span class="token imports"><span class="token punctuation">{</span> getPosts <span class="token punctuation">}</span></span> <span class="token keyword module">from</span> <span class="token string">'./utils/ghost'</span><span class="token punctuation">;</span> <span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Head</span></span> <span class="token keyword module">from</span> <span class="token string">'next/head'</span><span class="token punctuation">;</span> <mark><span class="token keyword module">import</span> <span class="token imports"><span class="token maybe-class-name">Link</span></span> <span class="token keyword module">from</span> <span class="token string">'next/link'</span><span class="token punctuation">;</span></mark> <span class="token keyword module">export</span> <span class="token keyword module">default</span> <span class="token keyword">function</span> <span class="token function"><span class="token maybe-class-name">Home</span></span><span class="token punctuation">(</span><span class="token parameter">props</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">(</span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span><span class="token plain-text">My First Blog</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>description<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>My personal blog created with Next.js and Ghost<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">Head</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>container mx-auto py-10<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-center text-3xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text">My Personal Blog</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>flex justify-center mt-10 <span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>ul</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-xl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token punctuation">{</span>props<span class="token punctuation">.</span><span class="token property-access">posts</span><span class="token punctuation">.</span><span class="token method function property-access">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">post</span><span class="token punctuation">)</span> <span class="token arrow operator">=></span> <span class="token punctuation">(</span> <mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>li</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span>post<span class="token punctuation">.</span><span class="token property-access">title</span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span></mark><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"><</span><span class="token class-name">Link</span></span> <span class="token attr-name">href</span><span class="token script language-javascript"><span class="token script-punctuation punctuation">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">post/</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>post<span class="token punctuation">.</span><span class="token property-access">slug</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span><span class="token punctuation">}</span></span><span class="token punctuation">></span></span><span class="token punctuation">{</span>post<span class="token punctuation">.</span><span class="token property-access">title</span><span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span><span class="token class-name">Link</span></span></span></mark><span class="token tag"><span class="token punctuation">></span></span><span class="token plain-text"> </span><mark><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>li</span><span class="token punctuation">></span></span></mark> <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>ul</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span><span class="token plain-text"> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword module">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">getStaticProps</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">const</span> posts <span class="token operator">=</span> <span class="token keyword control-flow">await</span> <span class="token function">getPosts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword control-flow">return</span> <span class="token punctuation">{</span> <span class="token literal-property property">props</span><span class="token operator">:</span> <span class="token punctuation">{</span> posts <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>Next.js 提供了一个 Link 组件,它允许客户端跳转。在你的 index.js 文件中,你从 next/link 中引入 Link 组件。在 </p> <li> 标签中,你将帖子标题嵌套在 Link 组件中。和 HTML 中的 anchor 标签一样,Link 标签也需要一个 href 属性。你将 slug 作为 href 属性的值传递进去。</p> <p>保存文件并通过运行命令 npm run dev 或 yarn dev 重启开发服务器。</p> <p>在浏览器中导航到 localhost:3000。现在,如果您点击任何帖子标题,您将导航到相应的博客文章。您的博客页面将显示如下:</p> <div> <img decoding="async" src="https://cdn.silicloud.com/blog-img/blog/img/65644c5aa4b2f92e6c71e017/144-0.png" class='post-images' alt="Individual blog page" title=""> </div> <p>您在第一步提供的标题和正文内容将显示在您的发布页面上。此示例的标题为“如何在Silicon Cloud上构建和部署您的博客”,内容摘自本教程的介绍部分。</p> <p>在这个步骤中,您添加了从 Ghost CMS 检索单个帖子内容的功能,将它们渲染为单独的页面,并从索引页面链接到每个帖子。您现在已经创建了一个完整的博客,可以从 Ghost 中获取文章。</p> <h2>结论</h2> <p>在本文中,您在Silicon Cloud Droplet上部署了Ghost作为内容管理系统。您使用Next.js创建了自己的静态生成站点,并将Ghost CMS连接到Next.js网站,以提供内容。由于这是一个静态生成的网站,在客户端上发送的JavaScript较少,从而提高性能和SEO。您还使用TailwindCSS对博客进行了自定义设计。</p> <p>目前,您的博客只在您的机器上运行。作为下一步,您可以在Silicon Cloud应用平台上部署它,以便其他人可以查看。要开始,请按照《部署Next.js应用到App平台》教程进行操作。</p> <p>你还可以给你的博客添加更多功能。例如,你可以使用Algolia实现搜索功能,使用n8n自动化发布文章的流程,或者添加按标签列出文章的功能。</p> </div> <div class="tags are-medium"> </div> </div> </article> <section id="comments" class="comments-area"> <div id="respond" class="comment-respond"> <div class="is-flex is-justify-content-space-between is-size-5 mt-6"> 发表回复 <small><a rel="nofollow" id="cancel-comment-reply-link" class="is-size-7 has-text-danger " href="/zh/blog/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8ghost%E5%92%8Cnext-js%E5%9C%A8digitalocean%E4%B8%8A%E6%90%AD%E5%BB%BA%E6%82%A8%E7%9A%84%E5%8D%9A%E5%AE%A2/#respond" style="display:none;">取消回复</a></small><span class="tag is-light has-text-primary is-medium comment-number">0</span> </div><hr/><form action="https://www.silicloud.com/zh/blog/wp-comments-post.php" method="post" id="commentform" class="" novalidate><p class="comment-notes help pb-3">Your email address will not be published. Required fields are marked <span class="required has-text-danger">*</span></p><div class="field"><div class="control"><textarea name="comment" class="textarea" aria-required="true" placeholder="Add Comment"></textarea></div></div><div class="field is-horizontal"><div class="field-label is-normal"><label class="label" for="author">Name <span class="required has-text-danger">*</span></label></div> <div class="field-body"><div class="field"><div class="control"><input id="author" class="input is-danger" name="author" type="text" value="" size="30" aria-required='true' /></div></div></div></div> <div class="field is-horizontal"><div class="field-label is-normal"><label class="label" for="email">Email <span class="required has-text-danger">*</span></label> </div><div class="field-body"><div class="field"><div class="control"><input id="email" class="input is-danger" name="email" type="email" value="" size="30" aria-required='true' /></div></div></div></div> <div class="field is-horizontal"><div class="field-label is-normal"><label class="label" for="url">Website</label> </div><div class="field-body"><div class="field"><div class="control"><input class="input" id="url" name="url" type="url" value="" size="30" /></div></div></div></div> <div class="field is-horizontal"><div class="field-label"><label class="label"></label></div><div class="field-body"><div class="field"><div class="control"><label class="checkbox is-size-7"><input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" value="yes"> Save my name and email in this browser for the next time I comment.</label></div></div></div></div> <p class="form-submit has-text-right"><button name="submit" type="submit" id="submit" class="button is-primary hvr-icon-bob"><span>发表评论</span><span class="icon"><span class="hvr-icon icon-chevron-up"></span></span></button> <input type='hidden' name='comment_post_ID' value='1077' id='comment_post_ID' /> <input type='hidden' name='comment_parent' id='comment_parent' value='0' /> </p><hr/></form> </div><!-- #respond --> </section> </div> </div> </div> </section> </main> <footer class="footer mt-6 py-4"> <div class="navbar is-transparent"> <div class="container"> <div class="navbar-item copyright pl-0"> All Rights Reserved ©2024 Silicon Cloud </div> <div id="main-menu" class="navbar-menu is-active"> <div class="navbar-end"> <div class="navbar-item copyright pr-0 is-hidden-desktop"> </div> </div> </div> </div> </div> </footer> <script type="text/javascript" src="https://www.silicloud.com/zh/blog/wp-content/themes/iknowledgebase/assets/js/script.min.js?ver=1.3.6" id="iknowledgebase-js"></script> <script type="text/javascript" src="https://www.silicloud.com/zh/blog/wp-includes/js/comment-reply.min.js?ver=6.4.3" id="comment-reply-js" async="async" data-wp-strategy="async"></script> <script type="text/javascript" src="https://www.silicloud.com/zh/blog/wp-includes/js/jquery/jquery.min.js?ver=3.7.1" id="jquery-core-js"></script> <script type="text/javascript" src="https://www.silicloud.com/zh/blog/wp-includes/js/jquery/jquery-migrate.min.js?ver=3.4.1" id="jquery-migrate-js"></script> </body></html> <!-- Page cached by LiteSpeed Cache 6.0.0.1 on 2024-03-01 06:37:33 -->