Skip to content

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/truedirectory
archive/archive/truedate
tags/tagsfalse
article/posts/truedirectory

show: false 会输出一个空数组给 VitePress,阻止它回退到其他路径的 sidebar 配置。

两种树类型:

  • directory:按文件系统的目录结构组织,目录名作为分组标题,文件按日期倒序排列。适合文章有明确分类的场景。
  • date:按年份→月份的时间线组织。适合归档浏览。

两种树按需生成——如果没有任何页面类型用到 date 树,文件系统只会被扫描一次。

不传第二个参数就是默认行为,和上面表格里写的一样。

首页组件

HomeLayout 显示三块内容:

  1. 标题和副标题:分别读取 VitePress config 的 titledescription。改配置就能改首页显示,不用动组件代码。
  2. 统计栏:文章总数、标签总数、最近更新日期。一行排开,信息密度高。
  3. 标签云:取出现次数最多的前 10 个标签,点击跳转到标签页并自动筛选。
  4. 最近文章:最新的 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 中的 titledatetagsorder,按日期倒序排列。传参可以自定义 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 组件重写页面逻辑就行。色彩、纹理、排版全部复用。