title | description | type | stub | service | i18nReady |
---|---|---|---|---|---|
Hashnode & Astro |
使用 Hashnode 作为 CMS 向你的 Astro 项目添加内容 |
cms |
false |
Hashnode |
true |
import { FileTree } from '@astrojs/starlight/components'; import PackageManagerTabs from '~/components/tabs/PackageManagerTabs.astro' import { Steps } from '@astrojs/starlight/components';
Hashnode 是一个托管 CMS,允许你创建博客或出版物。
Hashnode 公共 API 是一个 GraphQL API,允许你与 Hashnode 进行交互。本指南使用 graphql-request
,一个与 Astro 配合良好的简约 GraphQL 客户端,将你的 Hashnode 数据引入你的 Astro 项目。
要开始,你需要具备以下条件:
使用你选择的包管理器安装 graphql-request
包:
本指南使用 graphql-request
,一个与 Astro 配合良好的简约 GraphQL 客户端,将你的 Hashnode 数据引入你的 Astro 项目。
- 一个 Hashnode 博客
- 已安装了 graphql-request 包的 Astro 项目。
此示例将创建一个首页,列出所有文章并链接到动态生成的各个文章页面。
1. 要使用 `graphql-request` 包获取你的站点数据,请创建一个 `src/lib` 目录并新建两个文件 `client.ts` 和 `schema.ts`:<FileTree title="项目结构">
- src/
- lib/
- **client.ts**
- **schema.ts**
- pages/
- index.astro
- astro.config.mjs
- package.json
</FileTree>
-
使用来自你的 Hashnode 网站的 URL,初始化一个带有 GraphQLClient 的 API 实例。
import { gql, GraphQLClient } from "graphql-request"; import type { AllPostsData, PostData } from "./schema"; export const getClient = () => { return new GraphQLClient("https://gql.hashnode.com") } const myHashnodeURL = "astroplayground.hashnode.dev"; export const getAllPosts = async () => { const client = getClient(); const allPosts = await client.request<AllPostsData>( gql` query allPosts { publication(host: "${myHashnodeURL}") { title posts(first: 20) { pageInfo{ hasNextPage endCursor } edges { node { author{ name profilePicture } title subtitle brief slug coverImage { url } tags { name slug } publishedAt readTimeInMinutes } } } } } ` ); return allPosts; }; export const getPost = async (slug: string) => { const client = getClient(); const data = await client.request<PostData>( gql` query postDetails($slug: String!) { publication(host: "${myHashnodeURL}") { post(slug: $slug) { author{ name profilePicture } publishedAt title subtitle readTimeInMinutes content{ html } tags { name slug } coverImage { url } } } } `, { slug: slug } ); return data.publication.post; };
-
配置
schema.ts
来定义从 Hashnode API 返回的数据的结构。import { z } from "astro/zod"; export const PostSchema = z.object({ author: z.object({ name: z.string(), profilePicture: z.string(), }), publishedAt: z.string(), title: z.string(), subtitle: z.string(), brief: z.string(), slug: z.string(), readTimeInMinutes: z.number(), content: z.object({ html: z.string(), }), tags: z.array(z.object({ name: z.string(), slug: z.string(), })), coverImage: z.object({ url: z.string(), }), }) export const AllPostsDataSchema = z.object({ publication: z.object({ title: z.string(), posts: z.object({ pageInfo: z.object({ hasNextPage: z.boolean(), endCursor: z.string(), }), edges: z.array(z.object({ node: PostSchema, })), }), }), }) export const PostDataSchema = z.object({ publication: z.object({ title: z.string(), post: PostSchema, }), }) export type Post = z.infer<typeof PostSchema> export type AllPostsData = z.infer<typeof AllPostsDataSchema> export type PostData = z.infer<typeof PostDataSchema>
通过 getAllPosts()
获取的数据返回一个对象数组,包含每篇文章的属性,例如:
title
- 文章的标题brief
- 文章内容的 HTML 渲染coverImage.url
- 文章特色图片的源 urlslug
- 文章的 slug
使用从获取中返回的 posts
数组在页面上显示博客文章列表。
---
import { getAllPosts } from '../lib/client';
const data = await getAllPosts();
const allPosts = data.publication.posts.edges;
---
<html lang="en">
<head>
<title>Astro + Hashnode</title>
</head>
<body>
{
allPosts.map((post) => (
<div>
<h2>{post.node.title}</h2>
<p>{post.node.brief}</p>
<img src={post.node.coverImage.url} alt={post.node.title} />
<a href={`/post/${post.node.slug}`}>Read more</a>
</div>
))
}
</body>
</html>
<FileTree title="项目结构">
- src/
- lib/
- client.ts
- schema.ts
- pages/
- index.astro
- post/
- **[slug].astro**
- astro.config.mjs
- package.json
</FileTree>
-
导入并使用
getAllPosts()
和getPost()
从 Hashnode 获取数据,并为每篇文章生成单独的页面路由。--- import { getAllPosts, getPost } from '../../lib/client'; export async function getStaticPaths() { const data = await getAllPosts(); const allPosts = data.publication.posts.edges; return allPosts.map((post) => { return { params: { slug: post.node.slug }, } }) } const { slug } = Astro.params; const post = await getPost(slug); ---
-
利用每个
post
对象的属性为每个页面创建模板。下面的示例展示了文章标题和阅读时间,然后是完整的文章内容:--- import { getAllPosts, getPost } from '../../lib/client'; export async function getStaticPaths() { const data = await getAllPosts(); const allPosts = data.publication.posts.edges; return allPosts.map((post) => { return { params: { slug: post.node.slug }, } }) } const { slug } = Astro.params; const post = await getPost(slug); --- <!DOCTYPE html> <html lang="en"> <head> <title>{post.title}</title> </head> <body> <img src={post.coverImage.url} alt={post.title} /> <h1>{post.title}</h1> <p>{post.readTimeInMinutes} min read</p> <Fragment set:html={post.content.html} /> </body> </html>
:::note
<Fragment />
是 Astro 内置的一个组件,它可以帮助你避免使用不必要的包裹元素。当从 CMS(例如 Hashnode 或 WordPress)获取 HTML 时,这一点尤其有用。 :::
要部署你的网站,请访问我们的部署指南并按照你选择的托管提供商的指示操作。
- GitHub 上的
astro-hashnode