你希望你的博客具备哪些功能? 以下是我选择并实现的功能:
功能清单
- 可发布到 GitHub Pages 的静态网站
- MDX 支持:做为一个前端开发,需要在文章中嵌入一些 React 示例组件
- 搜索功能
- 目录 (Clerk 风格)
- 文章页面
- 带分页的文章列表
- 分类功能
- 系列功能
- 标签(开发中)
- 社交分享时的元数据: OpenGraph 和 Twitter Card
- 生成静态站点!
搭建步骤
FumaDocs 官方文档中已有一篇关于如何搭建博客的教程:Setup a Blog。我在此基础上实现了上面提到的功能。
你有两种方式来继续:
- 克隆我的项目仓库
- 或者,按以下步骤将功能集成到你自己的 Next.js 项目中
Install and Configure Fumadocs
安装并配置 Fumadocs
你需要一个已配置好的 Fumadocs 项目。如果你还没有,可以用以下命令快速创建:
npm create fumadocs-app
也可以选择手动安装 Fumadocs。
此时,你应该已有了基本配置文件:source.config.ts
和 lib/source.ts
配置 ShadCN 组件
如果你已经安装了 shadcn,可以跳过此步骤。
pnpm dlx shadcn@latest init
我们需要以下组件:
- button
- popover
- badge
- card
一键安装:
pnpm dlx shadcn@latest add button popover badge card
我们还需要一个 book 图标组件,用于展示系列文章:
pnpm dlx shadcn@latest add "https://21st.dev/r/designali-in/book"
定义博客集合(Collection)
我们使用 zod
来定义 frontmatter 的校验:
npm install zod
在 source.config.ts 中添加以下代码:
import { defineCollections, defineConfig, frontmatterSchema } from 'fumadocs-mdx/config'
export const posts = defineCollections({
type: 'doc',
dir: 'content/posts',
schema: frontmatterSchema,
})
在 lib/source.ts
中添加:
import { loader } from 'fumadocs-core/source'
import { createMDXSource } from 'fumadocs-mdx'
import { posts } from '@/.source'
export const postsSource = loader({
baseUrl: '/posts',
source: createMDXSource(posts),
})
添加自定义的 fumadocs 组件
由于功能较多,我们使用 giget 命令批量复制文件:
添加所有必要的组件
npx giget gh:liaoyio/liaoyi.im/src/fumadocs ./src/fumadocs --force
添加 /blog 路由页面
npx giget gh:liaoyio/liaoyi.im/src/app/\(home\)/posts ./src/app/\(home\)/posts --force
添加 /og 路由页面
npx giget gh:liaoyio/liaoyi.im/src/app/og ./src/app/og --force
导入schema 用于扩展文章集合默认的 frontmatter
import { postsSchema } from '@fumadocs/schema'
import { defineCollections, defineConfig, frontmatterSchema } from 'fumadocs-mdx/config'
export const posts = defineCollections({
type: 'doc',
dir: 'content/posts',
schema: frontmatterSchema.extend(postsSchema),
})
在 global.css
中添加样式:
@import "../fumadocs/css/blog.css";
配置博客信息
如果 src/fumadocs 是一个npm包,那么上述所有步骤都将由其处理。现在,我们需要配置博客。博客配置驱动以下内容:
- Metadata (元数据)
- Components (自定义组件)
- Categories (分类)
- Series (系列)
- Others (分页设置等)
我将记录一份详细的指南,但到目前为止,以下内容应该能给出大部分想法
在根路径的仓库中添加以下文件,我们使用 @/post-config
导入
import type { Metadata } from 'next'
import type { BlogConfig } from '@/fumadocs/types'
import { Brain, Code, Cog, Lightbulb, LucideBook, Megaphone, Rocket, Users, Wrench } from 'lucide-react'
import { Social } from '@/fumadocs/components/icons/social'
export const blogConfig = {
blogTitle: 'Blog',
blogDescription: 'Articles and thoughts',
siteName: 'liaoyi.im',
defaultAuthorName: 'liaoyi',
xUsername: '@liaoyia',
// Pagination
paginationTitle: (page: number) => `Blog - Page ${page}`,
paginationDescription: (page: number) =>
`Articles and thoughts - Page ${page}`,
categoryPaginationTitle: (category: string, page: number) =>
`${category.charAt(0).toUpperCase() + category.slice(1)} - Page ${page}`,
categoryPaginationDescription: (category: string, page: number) =>
`Articles in the ${category} category - Page ${page}`,
// URLs
blogBase: '/posts',
blogOgImageBase: 'og',
pageSize: 5,
}
export function createBlogMetadata(
override: Metadata,
blogConfig: BlogConfig,
): Metadata {
const { defaultAuthorName, siteName, xUsername } = blogConfig
const siteUrl = `https://${siteName}`
const author = {
name: defaultAuthorName,
url: siteUrl,
}
return {
...override,
authors: [author],
creator: defaultAuthorName,
openGraph: {
title: override.title ?? undefined,
description: override.description ?? undefined,
url: siteUrl,
siteName,
...override.openGraph,
},
twitter: {
card: 'summary_large_image',
site: xUsername,
creator: xUsername,
title: override.title ?? undefined,
description: override.description ?? undefined,
...override.twitter,
},
alternates: {
canonical: '/',
types: { 'application/rss+xml': '/api/rss.xml' },
...override.alternates,
},
icons: {
icon: [
{
media: '(prefers-color-scheme: light)',
url: '/icon.svg.svg',
href: '/icon.svg',
},
{
media: '(prefers-color-scheme: dark)',
url: '/icon.svg.svg',
href: '/icon.svg',
},
],
},
}
}
export function getCategoryBySlug(slug: string) {
const categories = {
'idea': {
label: 'Idea',
icon: Brain,
description:
'Exploratory thoughts and wild concepts for Teurons and beyond.',
}
}
return (
categories[slug as keyof typeof categories] || {
label: slug.toString().replace(/-/g, ' ').toLowerCase(),
icon: Social.github,
}
)
}
export function getSeriesBySlug(slug: string) {
const series = {
'x': {
label: 'Series X',
icon: LucideBook,
description:
'A comprehensive series on Zero Trust security architecture.',
},
// Add more series here as needed
}
return (
series[slug as keyof typeof series] || {
label: slug.charAt(0).toUpperCase() + slug.slice(1),
icon: LucideBook,
description: `Articles in the ${slug.charAt(0).toUpperCase() + slug.slice(1)} series.`,
}
)
}
添加测试文章
添加一个测试用 .mdx
文件,路径为 content/posts/idea/zero-trust-security.mdx
需要注意的点:"idea" 是博客的类别,"zero-trust-security" 将是文章的 URL。
---
title: Zero Trust Security
description: Why modern security architectures assume breach and verify everything
author: lina
date: 2025-03-22
tags: [security, zero trust, cybersecurity, enterprise]
---
# Zero Trust Security
Traditional security models operated on the principle of "trust but verify" and focused on perimeter defense. Zero Trust flips this paradigm with a simple principle: never trust, always verify.
## Core Principles
Zero Trust is built on several foundational ideas:
### Assume Breach
Zero Trust architectures operate under the assumption that attackers are already present within the network.
### Verify Explicitly
Every access request must be fully authenticated, authorized, and encrypted:
1. Strong identity verification for all users
2. Device health validation
3. Just-in-time and just-enough access
4. Context-aware policies
## Implementation Strategies
Moving to Zero Trust requires systematic changes:
### Identity as the Control Plane
Modern security centers on identity rather than network location:
### Micro-Segmentation
Network security shifts from perimeter-based to fine-grained segmentation between workloads.
测试博客功能
此时打开 http://localhost:3000/posts
,你将看到文章列表(目前只有一篇)。添加更多文章后,每页展示 5 篇,会自动分页。
点击文章可进入详情页:你可以查看标签、分类、系列等信息。
如果你想将文章归入某个系列,在 .mdx
文件中,可添加以下 frontmatter
字段:
series: building-react-component-library
seriesPart: 1
静态部署
刚开始搭建博客时,我的需求一个支持静态部署的站点,但是随着对 Next.js 全栈框架的探索,我的站点可能会演示一些 Next.js 框架独有的特性,并且必须在服务端运行,所以无法静态部署,关于静态部署部分,你可以查看 Next.js 项目静态部署到 GitHub Pages。
后续计划
本博客的开发是基于我个人的需求和正在构建的产品,以下是我接下来的计划:
- 添加订阅功能(但静态站点如何实现?)
- 标签功能
- 组件样式支持自定义覆盖
- 封装为可克隆模板
- 最大的问题在于,一旦你克隆模板并开始修改,后续就无法方便地更新。所以我考虑将其改为 monorepo 架构,以便以开源包的形式持续更新。
Last updated on