- 知识管理
- 知识库
【免费下载链接】dendron
The personal knowledge management (PKM) tool that grows as you do!
本文以
packages/nextjs-template发布模板中自带的features.collection.one.md测试笔记及其父级features.collection.md为线索,系统讲解 Dendron 的集合(Collection)功能:如何通过 frontmatter 把一组层级子笔记聚合为有序的文章列表、如何在 nextjs-template 静态站点中渲染为“集合页”,以及排序、日期、导航隐藏背后的源码实现。读完本文,你将能在自己的 Dendron 工作区中用has_collection、sort_by、sort_order等字段搭建博客归档、阅读清单或发布专栏,并能基于仓库源码理解其运行机制。
一、集合(Collection)是什么:从一个测试工作区说起
Dendron 以“层级 + frontmatter”组织个人知识库。当一个笔记层级下的多个子笔记需要被当作一组“条目”展示时(例如博客文章列表、书籍清单、FAQ 归档),就可以把该父笔记标记为集合(Collection),让发布端自动聚合其子笔记并渲染成有序列表。
packages/nextjs-template/fixtures/test-nextjs-workspace/vault/下的三个文件构成了一个最小的集合示例:
- 父笔记 features.collection.md:frontmatter 中带有
has_collection: true,正文为空; - 子笔记 features.collection.one.md:frontmatter 只含
id / title / desc / updated / created,正文为 “First entry”; - 子笔记 features.collection.two.md:结构与上面一致,正文为 “Second entry”。
也就是说,features.collection.one.md是集合features.collection的一个普通成员笔记。它的技术价值并不在正文(只有一行占位文字),而在于它作为“集合子条目”的最小合法形态:任何一篇要被某个集合聚合的笔记,只需要保证层级关系正确、frontmatter 完整,并在父笔记上开启has_collection即可。
二、集合的 frontmatter 配置详解
2.1 父笔记:开启聚合的关键字段
features.collection.md 的完整 frontmatter 如下:
--- id: 7o0c2j54l2cm6hglk572e5z title: Collection desc: '' updated: 1646420716602 created: 1646417315164 has_collection: true sort_by: date sort_order: reverse config: global: enableBackLinks: false ---其中与集合直接相关的字段:
| 字段 | 取值 | 作用 |
|---|---|---|
has_collection | true/false(可选) | 把当前笔记标记为集合,其子笔记将被聚合为列表渲染;同时该笔记的导航子项会被隐藏 |
sort_by | created/title(类型定义),fixture 中为date | 声明排序依据 |
sort_order | normal/reverse(可选) | 排序方向,reverse表示倒序(最新在前) |
config.global.enableBackLinks | true/false | 集合页级别的发布配置覆盖,此处关闭了反向链接显示 |
这些字段在类型层面对应packages/common-all/src/types/index.ts中的DendronSiteFM:
export type DendronSiteFM = { published?: boolean; noindex?: boolean; canonicalUrl?: string; nav_order?: number; nav_exclude?: boolean; nav_exclude_children?: boolean; permalink?: string; /** * If collection, don't show in nav * and have custom sorting rules */ has_collection?: boolean; /** * Default: created */ sort_by?: "created" | "title"; sort_order?: "reverse" | "normal"; skipLevels?: number; };两点需要注意:
has_collection的注释明确说明:开启后该笔记不出现在导航中,并启用自定义排序规则——这解释了为什么集合页通常不再以普通“目录”形式展开子节点;- 类型声明的
sort_by枚举为created/title,而 fixtures 中写了sort_by: date。从 nextjs-template 的实际渲染实现看(见本文第四节),排序实际按custom.date优先、created次之执行,date更多是一种面向发布语义的可读声明。编写自己的集合时,建议以类型定义的两个枚举值为准,并确保子笔记有明确的created(或date)字段。
2.2 子笔记:集合成员的最小 frontmatter
features.collection.one.md 的 frontmatter:
--- id: 01sqwbb94iwyfktm81pqpt6 title: One desc: '' updated: 1646417354923 created: 1646417351876 ---集合成员笔记只需要具备标准字段即可:id(全局唯一标识)、title(列表项标题)、desc(描述,可为空)、updated/created(纪元毫秒时间戳)。渲染集合列表时,发布端会读取这些字段生成每个条目的标题与日期;若需要更丰富的展示,还可给子笔记额外添加custom.date(ISO 日期字符串)与custom.excerpt(摘要),详见第四节。
三、发布链路:一篇集合子笔记如何变成页面
集合功能由 nextjs-template 的 Next.js 静态生成(SSG)流程驱动,整条链路可概括为:
getNotePaths(生成路径) → pages/notes/[id].tsx / pages/index.tsx 的 getStaticProps → prepChildrenForCollection(聚合 + 排序) → DendronNotePage(渲染正文 + 集合列表) → DendronCollectionItem(渲染单个条目)- pages/notes/[id].tsx 在每个笔记页的
getStaticProps中调用:
const collectionChildren = note.custom?.has_collection ? prepChildrenForCollection(note, notes) : null;utils/getStaticPropsUtil.tsx 在首页(index)的
getStaticProps中做了完全相同的判断。两个入口共用同一个prepChildrenForCollection,保证集合在任何页面下行为一致。渲染端 components/DendronNotePage.tsx:
const maybeCollection = note.custom?.has_collection && !_.isNull(collectionChildren) ? collectionChildren.map((child: NoteProps) => ( <DendronCollectionItem key={child.id} note={child} noteIndex={noteIndex} /> )) : null;随后maybeCollection被放置在笔记正文(DendronNote)之后、评论组件之前。也就是说:集合页 = 父笔记正文 + 自动生成的子条目列表,子条目之间是平铺的<article>而非嵌套目录。
四、排序与日期规则源码解析
集合列表的排序逻辑集中在 components/DendronCollection.tsx 的prepChildrenForCollection:
export function prepChildrenForCollection(note, notes) { if (note.children.length <= 0) { return null; } let children = note.children.map((id) => notes[id]); children = _.sortBy(children, (ent) => { if (_.has(ent, "custom.date")) { const dt = DateTime.fromISO(ent.custom.date); return dt.toMillis(); } return ent.created; }); if (_.get(note, "custom.sort_order", "normal") === "reverse") { children = _.reverse(children); } return children; }从中可以提炼出三条确定性的规则:
- 空集合返回
null:父笔记没有子节点时,页面不渲染集合区域; - 排序键:
custom.date优先,否则用created:若子笔记 frontmatter 存在date字段(ISO 格式),按DateTime.fromISO(...).toMillis()的毫秒值升序排列;否则按 frontmatter 的created毫秒时间戳排序; sort_order: reverse整体反转:默认normal,当父笔记设置reverse时对排序结果取反——fixture 中features.collection设置了sort_order: reverse,因此聚合后 “Two” 会排在 “One” 之前。
单个条目的渲染由同文件的DendronCollectionItem完成(DendronCollection.tsx):
- 链接:
getNoteUrl({ note, noteIndex })(见 utils/links.ts),根笔记指向/,其余指向/notes/${note.id}; - 标题:
note.title,输出为<h2 itemProp="headline">包裹的 Next.js<Link>; - 日期:若
custom.date存在则按 ISO 日期字符串格式化,否则把created的毫秒数格式化为短日期(DateTime.DATE_SHORT); - 摘要:仅当存在
custom.excerpt时才渲染<p itemProp="description">。
因此,若想让集合条目展示日期与摘要,需要在子笔记 frontmatter 中补充,例如:
--- id: xxx title: One desc: '' created: 1646417351876 date: 2022-03-05 excerpt: 这是一段出现在集合列表中的摘要文字 ---注意date与excerpt属于custom(自定义)字段命名空间,集合渲染实现通过custom.date/custom.excerpt读取(DendronCollection.tsx)。
五、集合对导航与正文层级的影响
开启has_collection后,Dendron 发布端会自动调整两处导航行为,避免“集合列表”与“目录树”重复展示。
5.1 侧边栏不再展开集合子节点
packages/common-all/src/sidebar.ts 生成侧边栏分类项时:
if (isCategory) { const shouldIgnoreChildren = fm.nav_exclude_children || fm.has_collection; return { type: "category", label: note.title, items: shouldIgnoreChildren ? [] : generateSidebar(children), link: { type: "note", id: note.id }, ... } as SidebarItemCategory; }即nav_exclude_children与has_collection只要有一个为真,侧边栏中该分类就不再递归展开子项。这正是 features.collection.md 这类集合页在导航中只显示为单个入口的原因。
该行为有对应的单元测试覆盖:engine-test-utils/src/tests/common-all/sidebar.spec.ts 中“WHEN has_collection is enabled”用例验证了开启has_collection后fooSidebarItem的items为[],且即使显式设置nav_exclude_children = false,子项依然保持为空——说明has_collection本身即构成隐藏子项的充分条件。
5.2 正文末尾不再自动追加子笔记列表
packages/unified/src/remark/hierarchies.ts 在页面正文底部生成“子笔记”区块时:
function addChildren() { // don't include if collection present if (!note || note.children.length <= 0 || note?.custom?.has_collection) { return; } ... }集合父笔记的正文底部不会再渲染传统的子笔记层级列表,避免与自动生成的集合条目列表重复。这也是“集合”与普通“目录页”在页面结构上的核心区别:前者由DendronCollectionItem按排序规则输出,后者按层级递归输出。
六、发布配置与端到端验证
6.1 发布侧配置
集合功能依赖 nextjs-template 的静态发布,工作区级配置见 fixtures/test-nextjs-workspace/dendron.yml:
version: 5 publishing: enableFMTitle: true enableMermaid: true enablePrettyRefs: true enableKatex: true copyAssets: true siteHierarchies: - root writeStubs: false siteRootDir: docs要点:siteHierarchies: [root]表示从root层级开始发布(fixture 的根笔记见 vault/root.md);集合所在的层级只要能被发布抓取,has_collection就会在页面渲染阶段生效。
6.2 E2E 测试中的集合场景
集合在导航面包屑场景下有专门的端到端验证:e2e/general.spec.ts:
test.describe("AND parent has property `has_collection` set", () => { test("THEN should display breadcrumbs", async ({ page, url }) => { await page.goto(`${url}/notes/rxOL3iDLtytHxCAraYSlg`); const breadcrumb = page.locator(".ant-breadcrumb"); await expect(breadcrumb).toHaveScreenshot([ "breadcrumb", "parent-has_collection.png", ]); }); });这条用例验证的是:即使父级是集合(子项被隐藏),从子笔记页面向上访问面包屑时导航依然正确。它说明集合只影响“子节点展开”,不影响层级间的跳转与面包屑路径。
七、实践建议与小结
基于以上分析,在 nextjs-template 发布场景下搭建你自己的集合,最小操作步骤如下:
- 建一个父笔记(如
blog.collection.md),frontmatter 中设置:has_collection: true sort_by: created sort_order: reverse - 在父笔记之下创建子笔记(如
blog.collection.one.md、blog.collection.two.md),每个子笔记确保id / title / created存在,并按需补充date、excerpt用于列表展示; - 执行发布构建(nextjs-template 的
buildStatic.js等脚本,见 packages/nextjs-template/scripts/buildStatic.js),打开集合父笔记页面,即可看到按规则排序的条目列表; - 如需调整方向,改
sort_order为normal;如子笔记较多想按标题排序,类型层面还声明了sort_by: title的取值空间,可结合 DendronSiteFM 验证发布效果。
一句话总结:features.collection.one.md这样的集合成员笔记本身很简单,真正的力量来自父笔记上的has_collection / sort_by / sort_order三个字段,它们驱动 nextjs-template 在构建期完成“子节点聚合 → 排序 → 列表渲染 → 导航隐藏”的完整闭环。掌握这一模式,你就能用 Dendron 的纯 Markdown 笔记体系,低成本维护结构清晰、自动排序的内容集合页面。
- 知识管理
- 知识库
【免费下载链接】dendron
The personal knowledge management (PKM) tool that grows as you do!
相关推荐
Polars 聚合实战指南:基于 `group_by` 的表达式聚合、分组内过滤与排序
Polars 聚合实战指南:基于 group_by 的表达式聚合、分组内过滤与排序 本文以 Polars 官方用户指南《Aggregation》为主体,围绕 g
数据分析大数据Apache Druid TopN 查询详解:近似排序聚合的原理、配置与实战
Apache Druid TopN 查询详解:近似排序聚合的原理、配置与实战 TopN 查询是 Apache Druid 原生查询语言中的一种高性能查询类型,用
数据库OLAP大数据后端Laf 云数据库 sort() 排序查询实战:基于 MongoDB 原生 API 的升降序与组合排序
Laf 云数据库 sort 排序查询实战:基于 MongoDB 原生 API 的升降序与组合排序 在 Laf 云数据库中, sort 是最常用的查询修饰器之一,
后端Serverless前端云原生
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考