Quartz v5 BasesPage 插件:将 Obsidian Bases(.base 文件)渲染为交互式数据库视图
【免费下载链接】quartz🌱 a fast, batteries-included static-site generator that transforms Markdown content into fully functional websites项目地址: https://gitcode.com/GitHub_Trending/qua/quartz
Obsidian Bases 是 Obsidian Desktop v1.8.0 起引入的数据库式笔记组织能力:通过.base文件定义对笔记库的查询,并将结果以表格、列表、卡片、看板、画廊等多种视图呈现。本篇文章以 Quartz v5 中的BasesPage插件(社区仓库quartz-community/bases-page)为核心,系统讲解它在 Quartz 中的定位、安装启用方式、.base文件的结构与配置项、与unlisted/加密页面的协作规则,以及它在 Page Type 分发器中的底层实现原理。读完本文,你将能够在自己构建的 Quartz 站点中启用 Bases 支持、编写与调优.base文件、扩展自定义视图,并理解这类数据库视图在静态站点中"构建期渲染"的特性与边界。
Bases 是什么:从.base文件到数据库视图
在 Obsidian 中,Bases 是介于纯笔记与数据库之间的产物。一个.base文件本身不包含笔记正文,而是包含一组查询定义:它声明要匹配哪些笔记(filter)、为笔记派生哪些计算属性(formula)、展示哪些列(property),以及用哪种视图呈现(view)。Quartz 通过BasesPage插件读取笔记库中的.base文件,解析其中的查询定义,把匹配到的笔记渲染成交互式、类似数据库的页面。
在功能层面,Quartz 对 Bases 的支持被归纳在 docs/features/Bases.md,该页面明确说明:
- Bases 文件定义对笔记库的查询,并在表格、列表、卡片、画廊、看板等可配置视图中显示结果;
- 支持由
BasesPage插件提供,配置选项、内置视图、表达式引擎与自定义视图扩展细节见插件页; - 安装命令为
npx quartz plugin add github:quartz-community/bases-page。
值得注意的是,BasesPage是一个Page Type + Component双重角色的插件:它既作为一种新的页面类型(.base文件会被单独渲染成页面),又提供渲染视图所需的组件。安装后,笔记库中每一个.base文件都会在构建时生成一个对应的数据库页面。
仓库自带的 docs/Base.base 就是一个完整可运行的真实示例——它把整个 docs 目录的文档作为数据源,生成了"全部文档、插件、组件与功能、最近更新、核心指南、看板、画廊、卡片、图片卡片"等多个视图。后续章节会以它为蓝本逐段剖析。
安装与启用
BasesPage属于社区插件(new-in-v5: true),需要显式安装。官方文档给出的安装命令是:
npx quartz plugin add github:quartz-community/bases-page安装后,在quartz.config.yaml的plugins列表中会出现如下默认配置项:
- source: github:quartz-community/bases-page enabled: true其中enabled: true表示默认启用;若暂时不需要 Bases 功能,将其改为false即可停用,无需删除配置条目。
在 Quartz v5 的默认模板中,BasesPage已被预置。查看 quartz/cli/templates/default.yaml 可以看到它位于插件列表中部,带order: 50:
- source: "@quartz-community/bases-page" enabled: true options: {} order: 50同时,在同一个模板的layout.byPageType段中专门登记了bases: {}这一页面类型(quartz/cli/templates/default.yaml),用于按页面类型定制 Bases 页的布局覆盖。blog.yaml、obsidian.yaml、ttrpg.yaml等其他模板同样内置了该插件,说明 Bases 支持是 v5 全模板共有的能力。
内置视图能力一览
BasesPage的核心竞争力在于丰富的内置视图类型与数据处理能力。根据插件文档,其功能矩阵如下:
| 能力 | 说明 |
|---|---|
| Table view(表格视图) | 可排序列,支持字符串、数字、布尔值、数组、链接等类型的自动渲染 |
| List view(列表视图) | 紧凑列表,每条记录附带元数据标签(chips) |
| Cards view(卡片视图) | 卡片式布局,可选支持图片属性 |
| Map view(地图视图) | 为未来的地图可视化预留的占位视图 |
| Multiple views(多视图) | 单个.base文件可定义多个视图,以可切换的标签页展示 |
| Filters(过滤器) | 支持and/or/not操作符的递归过滤树 |
| Formulas(公式) | 通过公式表达式计算派生属性 |
| Summaries(汇总) | 列级聚合,如 Sum、Average、Min、Max、Median 等 |
| Property configuration(属性配置) | 为属性自定义显示名称 |
| Link rendering(链接渲染) | 单元格内的 Wikilink 与 Markdown 链接渲染为可点击链接 |
其中 Table、List、Cards 三种视图有真实渲染器实现;Map 视图目前是占位。多视图、递归过滤树、公式与列级汇总共同构成了"把笔记库当数据库用"的完整工作流——例如对一批技术文档按 tag 分类、按最后修改时间排序、用公式把文档划分到不同分区,并同时以表格与卡片两种形态呈现。
.base文件结构详解
一个.base文件由filters、formulas、properties、views四个顶层段组成。下面以仓库内的真实文件 docs/Base.base 为例逐段说明(文件中的语法来自 Obsidian Bases 自身的查询语言,Quartz 侧原样解析执行)。
filters:数据筛选
filters段用递归表达式树描述"哪些笔记进入结果集"。and/or/not作为组合节点,叶子节点是比较表达式。例如"只收录 Markdown 文件":
filters: and: - file.ext == "md"组合示例(来自 docs/Base.base 的 Plugins 视图):命中任一 tag 即视为插件文档:
filters: or: - file.hasTag("plugin/transformer") - file.hasTag("plugin/emitter") - file.hasTag("plugin/filter")not节点用于排除:Core Guides 视图用一组长not列表把位于plugins、features、advanced、getting-started、cli、tags目录下的文档全部排除,剩下的就是核心指南(docs/Base.base)。Image Cards 视图则展示了字段级判断与取反(docs/Base.base):
filters: and: - file.folder == "plugins" - "!image.isEmpty()"该例同时用到了file.folder(笔记所在目录)与image.isEmpty()(frontmatter 图片字段是否为空)两个数据源字段。
formulas:派生属性
formulas段定义基于笔记元数据的计算属性,供后续属性列与视图分组使用。示例中定义了三个公式(docs/Base.base):
formulas: doc_type: | if(file.hasTag("plugin/transformer"), "transformer", if(file.hasTag("plugin/emitter"), "emitter", if(file.hasTag("plugin/filter"), "filter", if(file.hasTag("component"), "component", if(file.inFolder("features"), "feature", if(file.inFolder("advanced"), "advanced", if(file.inFolder("plugins"), "plugin", if(file.inFolder("getting-started"), "getting-started", if(file.inFolder("cli"), "cli", "guide"))))))))) last_modified: file.mtime.relative() section: | if(file.inFolder("plugins"), "plugins", if(file.inFolder("features"), "features", if(file.inFolder("advanced"), "advanced", if(file.inFolder("getting-started"), "getting-started", if(file.inFolder("cli"), "cli", if(file.inFolder("tags"), "tags", "core"))))))这里可以看到三类常用公式能力:
- 条件分支:
if(cond, a, b)支持多层嵌套,file.hasTag(...)判断标签,file.inFolder(...)判断所在目录; - 时间计算:
file.mtime.relative()输出相对修改时间,适合做"最近更新"类视图; - 多行字符串值:YAML 的
|块标量可以书写较长的表达式。
properties:列与显示名
properties段决定表格展示哪些列,并可为每个属性指定自定义显示名。属性名以file.*、note.*、formula.*或普通 frontmatter 字段名出现(docs/Base.base):
properties: title: displayName: Title formula.doc_type: displayName: Type formula.last_modified: displayName: Updated formula.section: displayName: Section即把title显示为 "Title",把formula.doc_type显示为 "Type" 等。这正是文档所述"自定义显示名称"能力的落地形态。
views:视图定义
views段是一个数组,每个元素是一个独立视图,运行时以标签页形式切换。共有字段包括type(视图类型)、name(标签名)、order(列顺序)、limit(条数上限);部分类型还支持groupBy(分组)、sort(排序)、filters(视图级过滤)、image(图片属性)、cardSize、imageAspectRatio等。逐条解读 docs/Base.base 中的示例:
- All Documentation(
type: table):最完整的表格视图,演示groupBy按formula.section升序分组、sort先按formula.doc_type再按file.name排序、以及columnSize定制列宽(docs/Base.base); - Plugins(
type: table):在文件级filters之外叠加视图级filters(or组合三个 plugin 类 tag),并按formula.doc_type分组(docs/Base.base); - Components & Features(
type: table):混合file.hasTag("component")与file.inFolder("features")两种条件(docs/Base.base); - Recently Updated(
type: list):列表视图,limit: 15只显示最近更新的 15 条(docs/Base.base); - Core Guides(
type: table):not过滤 +order列序(docs/Base.base); - By Type (Board)(
type: board):看板视图,按formula.doc_type分组为泳道(docs/Base.base); - Gallery(
type: gallery):画廊视图,limit: 30(docs/Base.base); - Cards(
type: cards):卡片视图,limit: 24(docs/Base.base); - Image Cards(
type: cards):卡片视图的图片变体,指定image: note.image取笔记 frontmatter 的图片字段,并用cardSize: 220与imageAspectRatio: 1控制卡片尺寸与宽高比(docs/Base.base)。
从该文件可以总结出视图字段的使用惯例:order决定列顺序;groupBy提供分组/泳道;sort用于表格多级排序;limit控制列表/画廊/卡片类视图的条数;filters让同一数据源在标签页级继续细分。
与unlisted页面的交互规则
BasesPage严格遵守 Quartz v5 的file.data.unlisted约定。该约定由 docs/plugins/UnlistedPages.md 定义:任意页面在 frontmatter 中写入unlisted: true,即可从所有"列表类"表面(contentIndex、搜索、图、资源管理器、反向链接、最近笔记、文件夹页、标签页等)中退出,同时页面仍以 HTML 形式发布、可通过直接 URL 访问。同样会写入该约定的还有 docs/plugins/EncryptedPages.md(加密页面默认或在unlistWhenEncrypted: true时)。
BasesPage的行为规则如下:
- 标记为
unlisted: true的页面,以及设置了stealth: true的加密页面,会从每一个渲染出的 Base 视图(表格、列表、看板、卡片、画廊及任何自定义视图)中排除——即使 Base 的过滤表达式本可以匹配到它们; unlisted页面不能被可见页面上的公式通过.asFile()解引用。
构建期渲染的结构性限制
插件文档特别强调了一个重要边界:Base 视图是构建时烘焙的服务器端渲染 HTML,不会在访问者解密某个加密页面后在客户端动态更新。
具体场景是:图(graph)、资源管理器(explorer)、搜索(search)在访问者成功解密一个可揭示的加密页面后,会从被修补的内存内容索引中重新水合,从而在本浏览器会话剩余时间内显示该页面;而 Base 视图在构建时已把 unlisted 页面排除,因此不会出现该页面——直到站点用该页面的公开状态重新构建。文档明确指出,这与反向链接、最近笔记、文件夹列表、标签列表面临的是同一个结构性限制(docs/plugins/BasesPage.md 中相关说明)。这一点在 docs/plugins/EncryptedPages.md 的"隐藏加密页面"一节中亦有交叉印证:服务端渲染的列表(backlinks、recent notes、tag pages、folder listings 以及 Bases 视图)在解密后仍然静态隐藏,因为它们是构建期烘焙的 HTML。
配置选项详解
BasesPage接受三个配置项,均配置在quartz.config.yaml插件条目的options下:
defaultViewType
- 类型:字符串
- 默认值:
"table" - 作用:当
.base文件未显式指定视图类型时使用的默认视图类型。设为"list"或"cards"可改变所有未声明类型视图的默认形态。
linkResolution
类型:字符串,取值
"absolute"|"relative"|"shortest"默认值:
"shortest"作用:视图渲染器解析内部链接的策略。必须与 docs/plugins/CrawlLinks.md 的
markdownLinkResolution保持一致。对照 CrawlLinks 的取值语义:absolute:相对于内容目录根部的路径;relative:相对于当前链接来源文件的路径;shortest:仅文件名,若不足以唯一定位则退化为完整绝对路径(CrawlLinks 默认值)。
两者不一致会导致 Base 视图内的链接解析结果与全站其他位置(反向链接、嵌入等)不一致,因此修改任一处时都应同步另一处。
customViews
- 类型:视图渲染器映射对象(key 为视图类型名)
- 默认值:
{} - 作用:以自定义渲染器覆盖同名内置视图,或注册全新的视图类型。需要 TypeScript 覆盖(override),即无法仅靠 YAML 完成。
默认配置与 TS 覆盖
仅用默认配置时,quartz.config.yaml写:
- source: github:quartz-community/bases-page enabled: true若需要自定义视图渲染器,则必须在quartz.ts中通过ExternalPlugin.BasesPage()做 TS 覆盖,且必须放在loadQuartzConfig()调用之前:
import * as ExternalPlugin from "./.quartz/plugins" // Must be placed before loadQuartzConfig() ExternalPlugin.BasesPage({ defaultViewType: "table", customViews: { myView: ({ entries, view, basesData, total, locale }) => { // return JSX }, }, })渲染器回调接收四个参数:entries(当前匹配的条目)、view(视图定义)、basesData(Bases 数据源)、total(条目总数)与locale(当前语言环境),返回 JSX 即该视图的渲染结果。customViews中的 key 与.base文件views[].type字段对应,从而支持在同一数据源上混用内置视图与自定义视图。
底层实现:Page Type 分发与虚拟页面
要理解 Bases 页面是如何生成的,需要回到 Quartz v5 的 Page Type 机制。核心实现位于 quartz/plugins/pageTypes/dispatcher.ts。
从源码结构看,PageTypeDispatcher(quartz/plugins/pageTypes/dispatcher.ts#L152)是统一的页面类型分发器,其emit过程分三个阶段执行:
- Phase 1——生成虚拟页面:遍历所有 page type 插件,对带
generate实现的类型调用pt.generate({ content, cfg, ctx }),把产出的虚拟页面(virtual pages)构建为ProcessedContent并推入ctx.virtualPages(quartz/plugins/pageTypes/dispatcher.ts#L175-L201)。Bases 页面、Canvas 页面、404 页面都属于这类虚拟页面; - Phase 2——渲染普通页面:对每个真实内容文件,按
pt.match(...)匹配的页面类型渲染为 HTML(quartz/plugins/pageTypes/dispatcher.ts#L213-L233); - Phase 3——渲染虚拟页面:把生成的虚拟页面输出为独立 HTML 文件(quartz/plugins/pageTypes/dispatcher.ts#L235-L247)。
其中populateVirtualPageHtmlAst(quartz/plugins/pageTypes/dispatcher.ts#L117-L150)会把每个虚拟页面的 Body 组件先渲染成 hast 树写入vfile.data.htmlAst,这样 Markdown 中的嵌入语法(如![[file.base]]、![[file.canvas]])就能把虚拟页面的内容内联进其他页面——这正是 docs/features/Bases.md 中 Demo 段落用![[Base.base]]把整个 Base 视图嵌入文档页的实现基础。源码注释也明确写着该机制服务于![[file.canvas]]、![[file.base]]这类转置引用(quartz/plugins/pageTypes/dispatcher.ts#L175-L176)。
页面类型匹配器提供了一组可组合的原语(扩展名匹配、slug 前缀、frontmatter 谓词、and/or/not组合等),见 quartz/plugins/pageTypes/matchers.ts;BasesPage 插件在安装后会向分发器注册自己的页面类型与匹配规则(match.ext(".base")一类),从而让.base文件在构建流水线中被识别并生成页面。
布局与页面框架
BasesPage文档明确说明它使用default页面框架,即带左右侧栏的三栏布局。对照 docs/layout.md 的页面框架表格,"default 框架被 ContentPage、FolderPage、TagPage、BasesPage 使用"——Bases 页与普通内容页共享同一套头部、beforeBody、正文、左右侧栏与页脚的槽位结构,因此已经安装在侧栏中的资源管理器、搜索、反向链接等组件会自然地出现在 Bases 页上。
框架解析的优先级为:YAML 配置覆盖(layout.byPageType.bases.template)→ 插件注册的框架 → 页面类型源码声明的frame属性 → 回退到"default"(docs/layout.md 中"框架如何解析"一节)。若想让 Bases 页使用无侧栏的full-width或minimal框架,可以在quartz.config.yaml中覆盖:
layout: byPageType: bases: template: full-widthAPI 速查
| 项 | 内容 |
|---|---|
| 类别 | Page Type, Component |
| 函数名 | ExternalPlugin.BasesPage() |
| 来源 | quartz-community/bases-page社区仓库 |
| 安装命令 | npx quartz plugin add github:quartz-community/bases-page |
| v5 新增 | 是(new-in-v5: true) |
| 必需 | 否(required: false),但默认模板中已启用 |
使用建议与边界提醒
综合插件文档、docs/Base.base 示例与分发器源码,给出一组实战建议:
- 先复用内置视图:表格、列表、卡片、看板、画廊已覆盖绝大多数"文档目录页""资源索引页"场景,先以 docs/Base.base 为模板修改
filters/formulas/views三处即可上线,无需写任何 TS; - 公式优先用于分类与排序:
file.hasTag()、file.inFolder()、file.mtime.relative()组合出的派生列能支撑"类型、分区、更新时间"三类最常见的索引维度; - 保持
linkResolution与 CrawlLinks 一致:这是文档明确要求的硬约束,否则 Base 视图内链接的解析结果会与站内其他位置分裂; - 正确理解 unlisted 的语义边界:Base 视图是构建期烘焙的,加密页解密后的动态揭示不会反映到 Base 视图;需要把某页面移出 Base 视图,应使用
unlisted: true/stealth: true后重新构建; - 自定义视图才需要 TS 覆盖:
customViews的渲染器回调签名(entries、view、basesData、total、locale)是扩展点,且ExternalPlugin.BasesPage()必须置于loadQuartzConfig()之前。
BasesPage把 Obsidian Bases 的"笔记即数据"理念完整地带进了静态站点:只要维护一份.base文件,构建时即可获得可排序、可分组、可过滤的多形态数据库页面,同时与 Quartz v5 的 unlisted 约定、加密页面、页面框架与转置嵌入机制无缝协作。
【免费下载链接】quartz🌱 a fast, batteries-included static-site generator that transforms Markdown content into fully functional websites项目地址: https://gitcode.com/GitHub_Trending/qua/quartz
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考