Appearance
inkpaper 功能详解
inkpaper 的功能不多,但每个都想清楚了再做。这篇把各个功能点拆开讲。
侧边栏配置
侧边栏是用得最多的导航元素,所以做了比较细的配置粒度。
generateSidebar 接受两个参数:文章目录路径和配置对象。
ts
import { generateSidebar } from '@inkpaper/vitepress/config'
generateSidebar(postsDir, {
home: { show: true, tree: 'directory' },
archive: { show: true, tree: 'date' },
tags: { show: false },
article: { show: true, tree: 'directory' },
})四种页面类型,每种可以独立配置:
| 页面类型 | 路径前缀 | 默认 show | 默认 tree |
|---|---|---|---|
| home | / | true | directory |
| archive | /archive/ | true | date |
| tags | /tags | false | — |
| article | /posts/ | true | directory |
show: false 会输出一个空数组给 VitePress,阻止它回退到其他路径的 sidebar 配置。
两种树类型:
- directory:按文件系统的目录结构组织,目录名作为分组标题,文件按日期倒序排列。适合文章有明确分类的场景。
- date:按年份→月份的时间线组织。适合归档浏览。
两种树按需生成——如果没有任何页面类型用到 date 树,文件系统只会被扫描一次。
不传第二个参数就是默认行为,和上面表格里写的一样。
首页组件
HomeLayout 显示三块内容:
- 标题和副标题:分别读取 VitePress config 的
title和description。改配置就能改首页显示,不用动组件代码。 - 统计栏:文章总数、标签总数、最近更新日期。一行排开,信息密度高。
- 标签云:取出现次数最多的前 10 个标签,点击跳转到标签页并自动筛选。
- 最近文章:最新的 10 篇文章,显示标题、日期、标签。列表项有交错的 fadeInUp 动画。
首页用 layout: doc 而不是 VitePress 默认的 home layout,因为我们需要 sidebar 和内容区的标准布局结构。
归档页组件
ArchivePage 把所有文章按年份分组,年份倒序排列。每个年份标题后面带文章数量。
实现上就是拿到 posts 数据,按 date.slice(0, 4) 分组,排序,渲染。没有分页——个人博客很少有上千篇文章的情况,全量渲染反而搜索体验更好。
标签页组件
TagsPage 分两个区域:
顶部是标签云,显示所有标签和对应的文章数。点击标签切换筛选,再点一次取消。当前选中的标签用朱砂色标记。
下面是文章列表,根据选中标签实时过滤。不选标签时显示全部文章。
标签页支持 URL query 参数:访问 /tags?tag=CSS 会自动选中 CSS 标签。首页标签云的链接就是用这个机制跳转的。
文章侧边栏
每篇文章的右侧边栏(outline 区域下方)显示三块信息:
字数统计和阅读时间:通过 DOM 文本节点计数实现,取 .content-container .vp-doc 元素的 textContent,去掉空白字符后统计长度。阅读时间按 400 字/分钟计算,至少显示 1 min。
路由切换时会重新计算,用 setTimeout 延迟 100ms 等 DOM 更新完成。
当前文章标签:从 frontmatter 读取,每个标签链接到标签页。
相关文章:推荐逻辑分两层。优先查找用户提供的 related.json,里面是手动或用算法预计算好的相关文章映射。如果没有,fallback 到标签交集:遍历所有文章,计算与当前文章共有标签数作为分数,取前 5 篇。
related.json 的格式:
json
{
"/posts/some-article": [
{ "link": "/posts/another-article", "title": "另一篇文章" }
]
}内容加载器
主题提供了 createPostsLoader 函数,从 @inkpaper/vitepress/loader 导入:
ts
import { createPostsLoader } from '@inkpaper/vitepress/loader'
export default createPostsLoader()它封装了 VitePress 的 createContentLoader,默认扫描 posts/**/*.md,提取 frontmatter 中的 title、date、tags、order,按日期倒序排列。传参可以自定义 glob 路径。
脚手架工具
@inkpaper/create-for-vitepress 是一个独立的 npm 包,用来快速初始化项目:
bash
npx @inkpaper/create-for-vitepress my-blog它会在目标目录下生成完整的项目结构:package.json、VitePress 配置、主题入口、三个页面、内容加载器、一篇示例文章。已有的文件不会被覆盖。
包结构
主题分成三个 npm 包:
@inkpaper/core 是纯 CSS,定义设计变量、字体加载、宣纸纹理、通用组件样式(文章列表、标签、aside 区块)。不依赖任何框架,理论上可以用在任何 HTML 页面里。
@inkpaper/vitepress 依赖 core,在此基础上提供 Vue 组件、Layout、数据注入(provide/inject)、sidebar 配置函数、内容加载器。所有 VitePress 特有的样式覆写(--vp-* 变量映射、.VPSidebarItem 样式等)都在这个包里。
@inkpaper/create-for-vitepress 是脚手架工具,用来生成项目模板。独立安装,不依赖上面两个包。
用户只需要安装 @inkpaper/vitepress,core 会作为依赖自动装上。
这个拆法是为了以后扩展。如果要做 Astro 版本,新建一个 @inkpaper/astro 包,导入 core 的 CSS,用 Astro 组件重写页面逻辑就行。色彩、纹理、排版全部复用。