页面属性 Alpha 测试
「页面属性」功能因受 Notion 页面属性的巨大启发而诞生:让 frontmatter 以一组简洁的属性形式展示、可编辑。
为什么
frontmatter 往往藏着最有用的元数据——状态、创建 / 更新时间、标签、阅读时长——却常被折叠。页面属性会把这些元数据放在标题下方,用简洁的布局和开发期的轻量编辑体验直接呈现。
功能
安装
通过运行以下命令将 @nolebase/vitepress-plugin-page-properties 安装到您的项目依赖项中:
ni @nolebase/vitepress-plugin-page-properties -Dpnpm add @nolebase/vitepress-plugin-page-properties -Dnpm install @nolebase/vitepress-plugin-page-properties -Dyarn add @nolebase/vitepress-plugin-page-properties -D快速上手
插件由两部分组成:
- Vite 插件:计算动态数据并在每个页面注入
<NolebasePageProperties />。 - Vue 插件:在 VitePress 主题中注册组件与样式。
配置 Vite 插件
在 VitePress 站点配置(或 vite.config.ts)里同时注册 PageProperties 和 PagePropertiesMarkdownSection。下面的示例与本仓库 docs/vite.config.ts 的接入方式一致:
如果你未曾见过这种红色绿色的标记
这是一种用于在用户界面(UI)上展示差异对比的标记规则。
红色的部分是你需要删除的,通常也以「减号 -」来表示,或者你可以理解为:这行文本原本的模样;
绿色的部分是你需要添加的,通常也以「加号 +」来表示,或者你可以理解为:这行文本将会被修改为的模样。
如果你想要了解更多有关「差异对比」的知识,你可以查看这篇有关 diffutils 历史的英文回答和 Git 的文档
是 TypeScript 用户吗?
至少需要配置下面的几个选项:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}其中的
module选项指定了 JavaScript/TypeScript 的模块格式,Nolebase Integrations 默认使用与ESNext相兼容的模块格式。moduleResolution选项指定了模块解析策略,因为所有的 Nolebase Integrations 插件都遵循最新的 ECMAScript 规范与导出声明,如果你遇到了Cannot find module ... or its corresponding type declarations的错误,你可能需要将moduleResolution设置为Bundler。
如果你想要更多的配置,可以参考下面的示例:
{
"compilerOptions": {
"jsx": "preserve",
"lib": [
"DOM",
"ESNext"
],
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmit": true,
"removeComments": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}import { join } from 'node:path'
import { defineConfig } from 'vitepress'
import {
PageProperties,
PagePropertiesMarkdownSection,
} from '@nolebase/vitepress-plugin-page-properties/vite'
export default defineConfig({
vite: {
plugins: [
PageProperties(),
PagePropertiesMarkdownSection({
excludes: [
join('pages', 'en', 'index.md'),
join('pages', 'zh-CN', 'index.md'),
],
}),
],
},
})PageProperties()会从每个 markdown 文件中计算动态数据(字数、阅读时长)。PagePropertiesMarkdownSection()会在第一个标题后自动插入<NolebasePageProperties />。需要跳过的页面可以用excludes/exclude(),也可以在 frontmatter 里写pageProperties: false或nolebase.pageProperties: false。
在主题里注册 UI 插件
在 docs/.vitepress/theme/index.ts 里安装 Vue 插件并引入样式:
如果你未曾见过这种红色绿色的标记
这是一种用于在用户界面(UI)上展示差异对比的标记规则。
红色的部分是你需要删除的,通常也以「减号 -」来表示,或者你可以理解为:这行文本原本的模样;
绿色的部分是你需要添加的,通常也以「加号 +」来表示,或者你可以理解为:这行文本将会被修改为的模样。
如果你想要了解更多有关「差异对比」的知识,你可以查看这篇有关 diffutils 历史的英文回答和 Git 的文档
是 TypeScript 用户吗?
至少需要配置下面的几个选项:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}其中的
module选项指定了 JavaScript/TypeScript 的模块格式,Nolebase Integrations 默认使用与ESNext相兼容的模块格式。moduleResolution选项指定了模块解析策略,因为所有的 Nolebase Integrations 插件都遵循最新的 ECMAScript 规范与导出声明,如果你遇到了Cannot find module ... or its corresponding type declarations的错误,你可能需要将moduleResolution设置为Bundler。
如果你想要更多的配置,可以参考下面的示例:
{
"compilerOptions": {
"jsx": "preserve",
"lib": [
"DOM",
"ESNext"
],
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"strict": true,
"strictNullChecks": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noEmit": true,
"removeComments": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"skipLibCheck": true
},
"include": [
"**/.vitepress/**/*.ts",
"**/.vitepress/**/*.mts",
"**/.vitepress/**/*.vue"
],
"exclude": [
"node_modules"
]
}import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import type { Theme as ThemeConfig } from 'vitepress'
import {
NolebasePagePropertiesPlugin,
} from '@nolebase/vitepress-plugin-page-properties/client'
import '@nolebase/vitepress-plugin-page-properties/client/style.css'
export const Theme: ThemeConfig = {
extends: DefaultTheme,
Layout: () => {
// 其它布局配置...
},
enhanceApp({ app }) {
app.use(NolebasePagePropertiesPlugin({
// 示例配置,可按需改成你的 frontmatter 字段
properties: {
'en': [
{ key: 'tags', type: 'tags', title: 'Tags' },
{ key: 'progress', type: 'progress', title: 'Progress' },
{ key: 'createdAt', type: 'datetime', title: 'Created at', formatAsFrom: true, dateFnsLocaleName: 'enUS' },
{ key: 'updatedAt', type: 'datetime', title: 'Updated at', formatAsFrom: true, dateFnsLocaleName: 'enUS' },
{ key: 'wordsCount', type: 'dynamic', title: 'Word count', options: { type: 'wordsCount' } },
{ key: 'readingTime', type: 'dynamic', title: 'Reading time', options: { type: 'readingTime', dateFnsLocaleName: 'enUS' } },
],
'zh-CN': [
{ key: 'tags', type: 'tags', title: '标签' },
{ key: 'progress', type: 'progress', title: '进度' },
{ key: 'createdAt', type: 'datetime', title: '创建时间', formatAsFrom: true, dateFnsLocaleName: 'zhCN' },
{ key: 'updatedAt', type: 'datetime', title: '更新时间', formatAsFrom: true, dateFnsLocaleName: 'zhCN' },
{ key: 'wordsCount', type: 'dynamic', title: '字数', options: { type: 'wordsCount' } },
{ key: 'readingTime', type: 'dynamic', title: '阅读时间', options: { type: 'readingTime', dateFnsLocaleName: 'zhCN' } },
],
},
}))
},
}描述属性 Schema
按语言声明希望展示的 frontmatter 字段,使标签、日期格式与 VitePress 的 i18n 设置一致。支持的属性类型:
tags:字符串数组渲染为标签,omitEmpty?: booleanplain:纯文本,omitEmpty?: booleanlink:链接字符串,omitEmpty?: booleanprogress:数字渲染为进度条,omitEmpty?: booleandatetime:日期时间,带formatAsFrom?: boolean、dateFnsLocaleName?: LocaleName、format?: string、omitEmpty?: booleandynamic:动态计算。可选{ type: 'wordsCount' }或{ type: 'readingTime', dateFnsLocaleName?: LocaleName }
下面的示例取自本仓库的预设(packages/unconfig-vitepress/src/plugins/nolebase/index.ts):
import type { Property } from '@nolebase/vitepress-plugin-page-properties/client'
const pagePropertiesOptions = {
properties: {
'en': [
{ key: 'tags', type: 'tags', title: 'Tags' },
{ key: 'progress', type: 'progress', title: 'Progress' },
{ key: 'createdAt', type: 'datetime', title: 'Created at', formatAsFrom: true, dateFnsLocaleName: 'enUS' },
{ key: 'updatedAt', type: 'datetime', title: 'Updated at', formatAsFrom: true, dateFnsLocaleName: 'enUS' },
{ key: 'wordsCount', type: 'dynamic', title: 'Word count', options: { type: 'wordsCount' } },
{ key: 'readingTime', type: 'dynamic', title: 'Reading time', options: { type: 'readingTime', dateFnsLocaleName: 'enUS' } },
] satisfies Property<keyof any>[],
'zh-CN': [
{ key: 'tags', type: 'tags', title: '标签' },
{ key: 'progress', type: 'progress', title: '进度' },
{ key: 'createdAt', type: 'datetime', title: '创建时间', formatAsFrom: true, dateFnsLocaleName: 'zhCN' },
{ key: 'updatedAt', type: 'datetime', title: '更新时间', formatAsFrom: true, dateFnsLocaleName: 'zhCN' },
{ key: 'wordsCount', type: 'dynamic', title: '字数', options: { type: 'wordsCount' } },
{ key: 'readingTime', type: 'dynamic', title: '阅读时间', options: { type: 'readingTime', dateFnsLocaleName: 'zhCN' } },
],
},
}tags、progress、datetime、link、plain会直接读取 frontmatter 值。dynamic类型(wordsCount、readingTime)会读取PageProperties()计算出的结果。
在 Markdown 中使用
在 frontmatter 中写入你声明过的字段即可。Markdown Section 插件会把属性列表放在第一个 # 标题下方:
---
title: 页面属性
tags:
- 集成/VitePress-插件
- Markdown/frontmatter
progress: 35
createdAt: 2024-01-23
updatedAt: 2024-01-23
---
# 页面属性
正文从这里开始……开发环境会自动切换到编辑器版本(<NolebasePagePropertiesEditor />),方便直接调整 frontmatter;生产环境则只渲染只读视图。
Neko
Rizumu