Hashnode & Astro
Hashnode 是一个托管 CMS,允许你创建博客或出版物。
与 Astro 集成
段落标题 与 Astro 集成Hashnode 公共 API 是一个 GraphQL API,允许你与 Hashnode 进行交互。本指南使用 graphql-request
,一个与 Astro 配合良好的简约 GraphQL 客户端,将你的 Hashnode 数据引入你的 Astro 项目。
前提条件
段落标题 前提条件要开始,你需要具备以下条件:
安装依赖
段落标题 安装依赖使用你选择的包管理器安装 graphql-request
包:
npm install graphql-request
pnpm add graphql-request
yarn add graphql-request
使用 Astro 和 Hashnode 创建博客
段落标题 使用 Astro 和 Hashnode 创建博客本指南使用 graphql-request
,一个与 Astro 配合良好的简约 GraphQL 客户端,将你的 Hashnode 数据引入你的 Astro 项目。
前提条件
段落标题 前提条件- 一个 Hashnode 博客
- 已安装了 graphql-request 包的 Astro 项目。
此示例将创建一个首页,列出所有文章并链接到动态生成的各个文章页面。
获取数据
段落标题 获取数据-
要使用
graphql-request
包获取你的站点数据,请创建一个src/lib
目录并新建两个文件client.ts
和schema.ts
:文件夹src/
文件夹lib/
- client.ts
- schema.ts
文件夹pages/
- index.astro
- astro.config.mjs
- package.json
-
使用来自你的 Hashnode 网站的 URL,初始化一个带有 GraphQLClient 的 API 实例。
src/lib/client.ts 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}") {titleposts(first: 20) {pageInfo{hasNextPageendCursor}edges {node {author{nameprofilePicture}titlesubtitlebriefslugcoverImage {url}tags {nameslug}publishedAtreadTimeInMinutes}}}}}`);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{nameprofilePicture}publishedAttitlesubtitlereadTimeInMinutescontent{html}tags {nameslug}coverImage {url}}}}`,{ slug: slug });return data.publication.post;}; -
配置
schema.ts
来定义从 Hashnode API 返回的数据的结构。src/lib/schema.ts 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>
生成页面
段落标题 生成页面-
创建页面
src/pages/post/[slug].astro
,为每篇文章动态生成页面。文件夹src/
- …
文件夹lib/
- client.ts
- schema.ts
文件夹pages/
- index.astro
文件夹post/
- [slug].astro
- astro.config.mjs
- package.json
-
导入并使用
getAllPosts()
和getPost()
从 Hashnode 获取数据,并为每篇文章生成单独的页面路由。src/pages/post/[slug].astro ---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
对象的属性为每个页面创建模板。下面的示例展示了文章标题和阅读时间,然后是完整的文章内容:src/pages/post/[slug].astro ---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><Fragment />
是 Astro 内置的一个组件,它可以帮助你避免使用不必要的包裹元素。当从 CMS(例如 Hashnode 或 WordPress)获取 HTML 时,这一点尤其有用。
发布你的网站
段落标题 发布你的网站要部署你的网站,请访问我们的部署指南并按照你选择的托管提供商的指示操作。
社区资源
段落标题 社区资源- GitHub 上的
astro-hashnode