news 2026/9/28 8:15:56

Dendron 集合(Collection)功能实战:基于 nextjs-template 的笔记聚合、排序与发布原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Dendron 集合(Collection)功能实战:基于 nextjs-template 的笔记聚合、排序与发布原理
  • 知识管理
  • 知识库

【免费下载链接】dendron

The personal knowledge management (PKM) tool that grows as you do!

项目地址:https://gitcode.com/gh_mirrors/de/dendron
点击查看免费下载

本文以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_collectiontrue/false(可选)把当前笔记标记为集合,其子笔记将被聚合为列表渲染;同时该笔记的导航子项会被隐藏
sort_bycreated/title(类型定义),fixture 中为date声明排序依据
sort_ordernormal/reverse(可选)排序方向,reverse表示倒序(最新在前)
config.global.enableBackLinkstrue/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; };

两点需要注意:

  1. has_collection的注释明确说明:开启后该笔记不出现在导航中,并启用自定义排序规则——这解释了为什么集合页通常不再以普通“目录”形式展开子节点;
  2. 类型声明的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; }

从中可以提炼出三条确定性的规则:

  1. 空集合返回null:父笔记没有子节点时,页面不渲染集合区域;
  2. 排序键:custom.date优先,否则用created:若子笔记 frontmatter 存在date字段(ISO 格式),按DateTime.fromISO(...).toMillis()的毫秒值升序排列;否则按 frontmatter 的created毫秒时间戳排序;
  3. 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 发布场景下搭建你自己的集合,最小操作步骤如下:

  1. 建一个父笔记(如blog.collection.md),frontmatter 中设置:
    has_collection: true sort_by: created sort_order: reverse
  2. 在父笔记之下创建子笔记(如blog.collection.one.md、blog.collection.two.md),每个子笔记确保id / title / created存在,并按需补充date、excerpt用于列表展示;
  3. 执行发布构建(nextjs-template 的buildStatic.js等脚本,见 packages/nextjs-template/scripts/buildStatic.js),打开集合父笔记页面,即可看到按规则排序的条目列表;
  4. 如需调整方向,改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!

项目地址:https://gitcode.com/gh_mirrors/de/dendron
点击查看免费下载

相关推荐

上一篇:Adobe-GenP 3.0 完整指南:免费一键修补 Adobe CC 2019-2023 全家桶
下一篇:用AssetStudio快速解包Unity资源

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 8:15:28

Agent四层能力拆解与Agentic RL训练工程化实践

1. 这不是又一篇“Agent概念科普”&#xff0c;而是实打实的模型能力拆解与训练路径复盘最近翻了二十多个开源Agent项目仓库、重跑了七套Agentic RL训练流程、在三个不同规模的仿真环境中反复验证策略收敛性&#xff0c;才敢把这篇东西写出来。标题里那个“2”字很关键——它不…

作者头像 李华
网站建设 2026/9/28 8:15:23

专精特新企业跨越四大能力鸿沟:从技术专精到组织强韧的破局路径

上个月我去一家做精密金属零部件的省级专精特新企业走访&#xff0c;老板在车间里指着一条新产线说&#xff0c;这几台设备是年初咬牙买回来的&#xff0c;现在调试了半年还没跑顺&#xff0c;因为能调这台进口设备的人&#xff0c;整个行业里也就那么几个。他这句话让我印象很…

作者头像 李华
网站建设 2026/9/28 8:15:19

网站开发公司简介怎么写:5步打造高转化介绍,教你怎么选对路径

网站开发公司简介怎么写:5步打造高转化介绍,教你怎么选对路径 不会代码想做网站?别慌。很多老板卡在第一步:网站开发公司简介怎么写。写得太虚,客户不信;写得太硬,没人看。更头疼的是,市面上模板满天飞,到底 怎么选 一份能打动客户的介绍?…

作者头像 李华
网站建设 2026/9/28 8:15:13

2026最新1元注册新域名避坑指南

2026最新1元注册新域名避坑指南 找建站公司怕被坑高价,其实域名注册才是第一道“宰客”陷阱。很多新手一上来就被忽悠买“顶级包”,结果每年续费要好几千,血本无归。2026最新的市场行情下,1元注册新域名并非全是噱头,而是各大平台争夺新用户的常规手段,但这里面的水很深。…

作者头像 李华
网站建设 2026/9/28 8:15:12

老网站不要了做新站需要怎么处理?保姆级建站教程

老网站不要了做新站需要怎么处理?保姆级建站教程 改个需求建站公司拖一周,这简直是很多老板的噩梦。看着手里那个代码臃肿、速度极慢、甚至被K站的老网站,你心里肯定在想:算了,推倒重来吧。…

作者头像 李华
网站建设 2026/9/28 8:14:23

农业行业网站模板哪家好?3招破解流量死局

农业行业网站模板哪家好?3招破解流量死局 网站做好了没人访问,这是很多农业企业老板最头疼的事。你花了几万块做的官网,图片清晰、文字优美,但后台数据惨淡,连个咨询电话都没有。这时候去搜“农业行业网站模板哪家好”,你会发现一堆营销号在吹嘘,却没人告诉你怎么让模板真正产生流量。…

作者头像 李华