zfy
August 26, 2026 · View on GitHub
🖊️ 本文档由 AI 辅助编写与修订,已结合项目源码人工校对;如有出入以实际代码为准。
🎯 前言
如果你在用 TriliumNext 做知识管理,大概率也想过把它变成博客——但默认共享页面太素了,又不想折腾 Hexo/WordPress 两套系统。
zfy 就是为这个做的:一个 TriliumNext 博客主题,基于 后端预处理 + EJS 输出 架构,所有内容在服务端预先生成 JSON 数据,模板只需读取静态数据,打开页面零等待。
🖼️ 示例博客 → brucevon.space
📑 目录
- ✨ 设计亮点
- 🖼️ 效果预览
- 🗺️ 架构速览
- 🚀 快速上手
- ⚙️ 预处理脚本
- 📦 压缩部署
- 🌐 nginx 配置参考
- ❓ 常见问题
- 📋 PromotedAttributes 完整配置
- 🏷️ 标签速查
- 📁 文件结构
- 💭 写在最后
✨ 设计亮点
| 🧩 双栏布局 | 左侧公告/动态/更新热力,右侧最新文章,PC 双栏、移动端单列 |
| 🏷️ 全标签驱动 | 20+ 个标签覆盖主题、评论、页脚、封面等全部配置,零硬编码 |
| 🌓 深色/浅色双主题 | CSS 变量 + data-theme 切换,一键切换 |
| 📱 响应式 | 768px 断点适配移动端,移动端单列布局 |
| 💬 Twikoo 评论 | CDN 加载,纯标签配置,主题自动跟随 |
| 🔍 全文搜索 | 服务端预生成索引,客户端实时全文检索,支持关键词高亮标记 |
| 🔥 热度地图 | 按年展示文章热力分布 |
| 📄 文章分页 | 首页最新文章一卡一篇,每页 5 篇分页浏览 |
| 🖼️ 图片灯箱 | 点击放大,大号左右切换按钮,支持键盘方向键 |
| 📦 单文件部署 | 提供压缩构建脚本,CSS/JS 内联合并为单个 EJS,部署只需同步一个笔记 |
| ⚡ 高性能 | 全静态输出 + SQL 批量化预处理,模板几乎零计算,一次聚合请求拉全站数据 |
🖼️ 效果预览
示例博客:brucevon.space
🗺️ 架构速览
配置流
根笔记(#isHome=true 的笔记)
│
│ blog.ejs 读取标签 → _cfg 对象
│
├─→ 服务端使用:主题、封面、分类树根、About 查找
│
└─→ window.__BLOG_CONFIG__ 注入到页面
│
└─→ blog.js 读取:HOME_ID、默认主题
预处理流程
BlogPreprocessRender.js (编排器)
├─ data.js ─→ 聚合数据(tree / aboutTree / tags / article /
│ recentUpdate / announcement / recommend / stats / heatmap)
└─ search.js ─→ 搜索索引 JSON(全量笔记)
│
└─→ 写入中间笔记(#shareRaw + #shareAlias=blog-data / blog-search)
│
└─→ 前端 JS 一次性拉取 /blog-data(聚合)+ /blog-search(搜索)
💡 理解这个整体流程后再按下方步骤操作,会更有方向感。
🚀 快速上手
💡 前置说明:Trilium 中"开启分享"指右键笔记 → Share → 笔记 URL 变为可公开访问。以下步骤中所有带"开启分享"的笔记都需做此操作。
1️⃣ 下载资源文件
从本仓库的 share/ 目录下载以下文件(模板 + 样式 + 脚本三个源文件,或直接用压缩后的单文件):
share/blog.ejs # 博客模板(主入口)
share/css/blog.css # 全部博客样式
share/js/blog.js # 客户端交互脚本
# 或使用单文件产物(内含压缩后的 css/js):
share/blog.min.ejs # ★ 压缩单文件(由 build-min.js 生成,推荐部署用)
Twikoo 使用 CDN,无需上传
twikoo.min.js。
2️⃣ 导入 Trilium(模板与样式)
首先 Trilium 中需要有一个 分享 主笔记(首次开启分享时自动创建,也可手动创建)。在其下或平级位置创建资源笔记,每个右键 → 开启分享:
方式 A:使用单文件产物(推荐)
| 文件 | 必配标签 |
|---|---|
blog.min.ejs | ~shareTemplate(inheritable)=blog.min.ejs(这是 关系 Relation,需在根笔记上通过 Relation Map 链接到此笔记) |
只需 1 个分享笔记即可完成整站模板 + 样式 + 脚本部署。
方式 B:使用三件套源文件
| 文件 | 必配标签 |
|---|---|
blog.ejs | ~shareTemplate(inheritable)=blog.ejs(关系 Relation,在根笔记 Relation Map 链接) |
blog.css | #shareAlias=blog.css #shareRaw |
blog.js | #shareAlias=blog.js #shareRaw |
静态资源路径为
/blog.css、/blog.js,假设 nginx 已隐藏/share/前缀。若未隐藏需相应调整(见下文 nginx 示例)。两种方式二选一。源码更新后可用
node build-min.js重新生成单文件产物。
3️⃣ 创建图片资源
博客需要以下 5 张图片,在 Trilium 中新建图片笔记(Upload file),每个开启分享:
| 图片 | 说明 |
|---|---|
favicon.ico | 浏览器标签页图标 |
logo.icon | 顶部栏左侧头像 |
bg-pc.png | PC 端博客背景图 |
bg-mobile.png | 移动端博客背景图 |
beian.png | 页脚备案图标(无备案可省略) |
图片分享后的 URL 为
/share/[noteId],nginx 隐藏前缀后变为/favicon.ico等。也可在根笔记标签中自定义路径。
4️⃣ 创建共享数据笔记
预处理脚本会把数据写入专用的 JSON 笔记,需要提前建好。在 分享 笔记下新建一个笔记(如 home-data),单独开启分享。在其下创建 2 个 json 类型子笔记:
| 子笔记 | 必需标签 |
|---|---|
blog-data | #shareHiddenFromTree #shareRaw #shareAlias=blog-data |
blog-search | #shareHiddenFromTree #shareRaw #shareAlias=blog-search |
💡 聚合接口
/blog-data一次性返回分类树、关于树、标签云、文章、动态、公告、推荐、统计、热力全部数据;/blog-search单独承载搜索索引(数据量大、更新频繁,故独立)。每条笔记的 noteId(右键 → 复制 ID)在后面配置脚本标签时会用到,先统一记下来,后续加到对应脚本的
#saveNoteId标签上。
5️⃣ 配置根笔记
新建根笔记(如 我的博客),右键 → 开启分享。在根笔记上添加 PromotedAttributes(见下文),确保设置以下关键标签:
#isHome=true— 声明此笔记为博客主页#homeId=当前笔记ID— 填入根笔记自身的 noteId(右键 → 复制 ID)#blogTitle=xxx、#blogDescription=xxx— 博客标题与副标题~shareTemplate(inheritable)=blog.ejs(或blog.min.ejs) — 关系 Relation,在 Relation Map 中搜索模板笔记并链接~shareFavicon(inheritable)=favicon.ico— 关联 favicon 图片笔记~shareLogo(inheritable)=logo.icon— 关联 logo 图片笔记
⚠️
~开头的标签是关系(Relation),不是普通标签。需要在根笔记的 Relation Map 中手动搜索并链接到目标笔记,直接粘贴文本不会自动建立连接。
6️⃣ 创建「关于」笔记
"关于"菜单树由 data.js 从根笔记下的「关于」笔记构建。需在根笔记下创建一个标题为 「关于」 的笔记,可包含子笔记并添加 #category=true 标签。
💡 如果暂时不需要"关于"菜单,建议至少创建一个空白的"关于"笔记,否则 data.js 会输出空的 aboutTree。
7️⃣ 发布文章
在根笔记下按需创建子笔记,添加以下标签控制展示位置:
| 标签 | 展示位置 |
|---|---|
#recommend=true | 首页"推荐阅读"模块 |
#article=true | 首页"最新发布"排序 |
#category=true | 分类树节点 |
#recentUpdate=true | 首页"动态"模块 |
#announcement=true | 首页公告区 |
8️⃣ 导入预处理脚本(可选但强推荐)
从 博客预处理控件/ 目录导入脚本文件:
BlogPreprocessRender.js→ 导入为 JSX 笔记(代码类型选JSX),新建一个笔记,类型选择 渲染笔记,点击渲染笔记选择JSX笔记,然后打开渲染笔记就可以看见渲染面板data.js、search.js→ 导入为 JS 后端脚本(Backend Script),建议导入为JSX笔记的子笔记,方便管理。
导入后参照下文 ⚙️ 预处理脚本 配置编排器。运行一次后,首页数据即预生成完毕。
注意:3 个脚本类型不同,不能全部选同一类型导入。
BlogPreprocessRender.js必须是 JSX 才能正常渲染面板。~renderNote也是 Relation,需在 Relation Map 中链接。
⚙️ 预处理脚本(核心架构)
首页的数据并非由 EJS 实时查询——而是通过 后端预处理脚本 在运行时一次性生成 JSON,写入独立的共享笔记,模板 / 前端 JS 只做静态读取。
数据流
预处理脚本(批量 SQL + SUBSTR 截取)
↓ api.note.setContent()
独立共享 JSON 笔记(#shareRaw + #shareAlias=blog-data / blog-search)
↓ 浏览器通过自定义路由获取
前端 JS(fetch('/blog-data') 一次性拉取聚合数据;fetch('/blog-search') 加载搜索索引)
脚本列表
从 博客预处理控件/ 目录下载全部文件,在各笔记中粘贴为 JS 后端脚本(Backend Script)。每个脚本通过 #saveNoteId 标签指向对应的 json 目标笔记:
| 脚本 | 功能 | 必需标签 |
|---|---|---|
BlogPreprocessRender.js | 编排入口,JSX 渲染笔记,显示一键同步按钮面板 | 需通过 ~renderNote 关联(见下文) |
data.js | 聚合脚本:一次生成分类树 / 关于树 / 标签云 / 文章 / 动态 / 公告 / 推荐 / 统计 / 热力全部数据 | #rootNoteId #saveNoteId #contentLen(可选) |
search.js | 搜索索引(全量笔记) | #rootNoteId #saveNoteId #contentLen(可选) |
每个脚本通过
#saveNoteId标签指向之前创建的对应 json 笔记的 ID。编排器会从每个子脚本标签中读取此 ID,写入对应笔记。
⚡ 性能优化说明
- SQL 批量化:脚本使用
JOIN blobs + SUBSTR在 SQLite 层面截取内容,避免 N 次api.getNote()循环拉取完整大 blob - 一次聚合查询:
data.js将全部模块合并为 2~3 条 SQL 查询(笔记查询 + 属性聚合 + 条件统计),并在 SQL 内完成标签 JSON 聚合 - 单次条件聚合:统计项用
COUNT(DISTINCT CASE WHEN ...)一次查询得出 - 传输量控制:前端仅 2 个请求(
/blog-data聚合 +/blog-search搜索),每篇笔记内容默认截断 ~150 字
设置编排器
-
确认已按 快速上手 → 第 4 步 创建好 2 个共享数据笔记(
blog-data、blog-search),每个均已正确添加#shareHiddenFromTree#shareRaw#shareAlias=blog-xxx标签 -
新建一个笔记(如
博客预处理面板),关联BlogPreprocessRender.js作为渲染笔记:- 将
BlogPreprocessRender.js导入为 JS 前端笔记(代码类型选JS Frontend) - 在新笔记上添加关系
~renderNote=BlogPreprocessRender.js(在 Relation Map 中搜索并链接) - 打开此渲染笔记即可看到一键同步按钮面板
- 将
-
将
BlogPreprocessRender.js设为父笔记,data.js、search.js作为其子笔记(子脚本笔记的标题须与上表"脚本"列一致,例如data.js) -
在每个子脚本笔记上添加标签:
子脚本 必需标签 data.js#saveNoteId=blog-data笔记ID#rootNoteId=博客根笔记ID#contentLen=150(可选)search.js#saveNoteId=blog-search笔记ID#rootNoteId=博客根笔记ID#contentLen=500(可选)编排器读取子脚本笔记上的
#saveNoteId标签确定写入目标。#rootNoteId用于树/标签等需要递归查询的数据。#contentLen覆盖默认截取长度。 -
打开渲染笔记,点击一键同步或逐个运行,检查各脚本控制台输出是否通过
💡 首次运行时建议逐个执行,观察控制台输出排查问题。成功后可使用一键同步批量生成全部数据。
自动执行
编排器及子脚本支持通过 #run 标签实现自动执行,具体参考 Trilium 后端脚本事件文档。例如 #run=sync 可在同步后自动触发预处理。
🔧 独立运行脚本(不使用编排器)
如果不使用编排面板,每个脚本也可以独立运行:
- 将脚本导入为 JS 后端脚本,在脚本笔记上添加必要标签(
#rootNoteId、#saveNoteId、#contentLen等) - 在 Trilium 中右键脚本笔记 → Execute script,脚本会读取自身标签运行
脚本的 standalone 块会自动检测
api._syncConfig是否已由编排器设置,如果未设置则从自身标签读取,无需额外配置。
📦 压缩部署
为避免部署时在 Trilium 中维护多个分享笔记,仓库提供压缩构建脚本:
- 源码(可读):
share/blog.ejs+share/css/blog.css+share/js/blog.js - 产物(压缩内联):
share/blog.min.ejs(esbuild 压缩 css/js 后内联进模板,自检通过)
node build-min.js # 需要 Node.js ≥ 16,首次运行自动拉取 esbuild
详细说明见 压缩部署说明.md。
🌐 nginx 配置参考(生产环境)
以下是一个生产级 nginx 反代配置(Trilium 运行在 http://127.0.0.1:8080,域名 brucevon.space):
# ==========================================
# 1. 定义上游服务器
# ==========================================
upstream trilium {
zone trilium 64k;
server 127.0.0.1:8080;
keepalive 2;
}
# ==========================================
# 2. HTTP → HTTPS 强制跳转
# ==========================================
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
# ==========================================
# 3. HTTPS 核心只读安全站点
# ==========================================
server {
listen 443 ssl http2;
server_name yourdomain.com;
ssl_certificate /etc/nginx/ssl/yourdomain.com/cert.pem;
ssl_certificate_key /etc/nginx/ssl/yourdomain.com/key.pem;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
proxy_intercept_errors on;
# Gzip 极限压缩
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
gzip_proxied any;
# 404/50x 错误页面导向
error_page 404 502 503 504 /404.html;
location = /404.html {
root /opt/apps/nginx/error_pages;
internal;
}
# 🔥 安全拦截:只精准拦截登录和明细后台操作
location ~* /(login|setup|admin|ws|custom|inside) {
return 404;
}
# 代理 /share/assets/ → 后端原路径(保留)
location /share/assets/ {
proxy_pass http://trilium;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_read_timeout 90;
}
# 🔙 核心反代:隐藏 /share/ 前缀
location / {
proxy_pass http://trilium/share/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cookie_path /share/ /;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_read_timeout 90;
# 专为预处理大 JSON 优化的全内存缓冲区
proxy_buffer_size 512k;
proxy_buffers 4 512k;
proxy_busy_buffers_size 1024k;
proxy_max_temp_file_size 0;
}
}
关键策略:
location /→proxy_pass http://trilium/share/自动隐藏/share/前缀,所有静态资源、JSON 数据和页面共用同一路由,无需 regex 分别匹配。安全拦截只针对/login|setup|admin|ws|custom|inside等后台路径,不会误伤/api/attachments/等图片资源。
❓ 常见问题
页面空白或报错
- 检查根笔记是否已开启分享(右键 → Share)
- 检查
blog.ejs(或blog.min.ejs)的~shareTemplate关系是否正确链接 - 检查所有资源笔记(css/js/图片)是否已开启分享
分类树/关于菜单不显示
- 确认
#rootNoteId标签指向正确的博客根笔记 ID(data.js 需要) - 确认根笔记下存在标题为 「关于」 的笔记
- 确认
#category=true已添加到需要展示的分类笔记上
搜索无结果
- 确保 search.js 已成功运行并写入 blog-search 笔记
- 检查
#rootNoteId是否指向包含所有文章的正确根笔记
样式错乱
- 清除浏览器缓存(
Ctrl+F5硬刷新) - 使用源码三件套时,确认
blog.css和blog.js的分享状态正常 - 检查 nginx 配置中静态资源路径是否映射正确
脚本报错 "content.trim is not a function"
- 如果使用旧版脚本,请更新至最新版本。该问题已在
39afe5f修复(blob 内容 Buffer→String 转换)。
📋 PromotedAttributes 完整配置
在 Trilium 根笔记上使用 PromotedAttributes 声明博客标签体系。以下分 4 类列出完整定义。
⚠️ 注意:
~relation类型需手动创建关系链接,复制文本不会自动建立连接。请在根笔记的 Relation Map 中搜索并链接到目标笔记。所有
#label:+ 标签值全部写在一行,空格分隔——下面按类别拆开展示是为了阅读方便,实际粘贴时请合为一行。
一、关系 Relations
~shareTemplate(inheritable)=blog.ejs ~shareFavicon(inheritable)=favicon.ico ~shareLogo(inheritable)=logo.icon
若使用压缩单文件部署,将
blog.ejs替换为blog.min.ejs。
二、根笔记专属标签(不继承)
#label:isHome="promoted,alias=博客主页,single,boolean" #isHome=true
#label:homeId="promoted,alias=博客主页ID,single,text" #homeId="笔记ID"
#label:blogTitle="promoted,alias=博客标题,single,text" #blogTitle="我的博客"
#label:blogDescription="promoted,alias=博客副标题,single,text" #blogDescription="子非鱼,安知鱼之乐"
#label:appearanceDefaultTheme="promoted,alias=博客默认主题,single,text" #appearanceDefaultTheme="dark"
#label:coverDefaultImage="promoted,alias=PC端背景URL,single,text" #coverDefaultImage="/bg-pc.png"
#label:coverMobileImage="promoted,alias=移动端背景URL,single,text" #coverMobileImage="/bg-mobile.png"
#label:twikooEnabled="promoted,alias=评论系统,single,boolean" #twikooEnabled=true
#label:twikooEnvId="promoted,alias=Twikoo环境ID,single,text" #twikooEnvId="你的EnvId"
#label:twikooVersion="promoted,alias=Twikoo版本,single,text" #twikooVersion="1.6.41"
#label:footerCopyright="promoted,alias=页脚版权,single,text" #footerCopyright="© 2026 YourName"
#label:footerIcp="promoted,alias=工信部备案号,single,text" #footerIcp="你的ICP备案号"
#label:footerPolice="promoted,alias=公安备案号,single,text" #footerPolice="你的公安备案号"
#label:footerPoliceUrl="promoted,alias=公安备案链接,single,text" #footerPoliceUrl="你的公安备案链接"
#label:footerBeianIcon="promoted,alias=备案图标URL,single,text" #footerBeianIcon="/beian.png"
#label:siteStartDate="promoted,alias=建站日期,single,text" #siteStartDate="2026-04-10"
根笔记系统标签
#readOnly #shareDescription="我的博客描述" #iconClass="bx bxs-yin-yang"
三、子笔记标签(继承 · 可选)
以下定义在根笔记上,(inheritable) 使其对子笔记可见:
#label:recommend(inheritable)="promoted,alias=推荐阅读,single,boolean"
#label:article(inheritable)="promoted,alias=文章,single,boolean"
#label:recentUpdate(inheritable)="promoted,alias=动态,single,boolean"
#label:announcement(inheritable)="promoted,alias=公告,single,boolean"
#label:enableTwikoo(inheritable)="promoted,alias=评论,single,boolean"
#label:category(inheritable)="promoted,alias=类别,single,boolean"
#label:shareHiddenFromTree(inheritable)="promoted,alias=隐藏,single,boolean"
#label:iconClass(inheritable)="promoted,alias=图标,single,text"
#label:dateNote(inheritable)="promoted,alias=日期覆盖,single,text"
#label:shareAlias(inheritable)="promoted,alias=别名,single,text"
子笔记标签只需定义
#label:元信息,无需在根笔记上设默认值。子笔记使用时自行添加并赋值,如#recommend=true。
四、一键复制(含以上全部)
💡 使用方式:下面按功能拆为 5 段方便阅读。实际使用时,将 5 段文本拼接成 1 行(空格分隔) 后粘贴到根笔记的标签框即可。
#label:isHome="promoted,alias=博客主页,single,boolean" #isHome=true #label:homeId="promoted,alias=博客主页ID,single,text" #homeId="笔记ID" #label:blogTitle="promoted,alias=博客标题,single,text" #blogTitle="我的博客" #label:blogDescription="promoted,alias=博客副标题,single,text" #blogDescription="子非鱼,安知鱼之乐"
#label:appearanceDefaultTheme="promoted,alias=博客默认主题,single,text" #appearanceDefaultTheme="dark" #label:coverDefaultImage="promoted,alias=PC端背景URL,single,text" #coverDefaultImage="/bg-pc.png" #label:coverMobileImage="promoted,alias=移动端背景URL,single,text" #coverMobileImage="/bg-mobile.png"
#label:twikooEnabled="promoted,alias=评论系统,single,boolean" #twikooEnabled=true #label:twikooEnvId="promoted,alias=Twikoo环境ID,single,text" #twikooEnvId="你的EnvId" #label:twikooVersion="promoted,alias=Twikoo版本,single,text" #twikooVersion="1.6.41"
#label:footerCopyright="promoted,alias=页脚版权,single,text" #footerCopyright="© 2026 YourName" #label:footerIcp="promoted,alias=工信部备案号,single,text" #footerIcp="你的ICP备案号" #label:footerPolice="promoted,alias=公安备案号,single,text" #footerPolice="你的公安备案号" #label:footerPoliceUrl="promoted,alias=公安备案链接,single,text" #footerPoliceUrl="你的公安备案链接" #label:footerBeianIcon="promoted,alias=备案图标URL,single,text" #footerBeianIcon="/beian.png" #label:siteStartDate="promoted,alias=建站日期,single,text" #siteStartDate="2026-04-10"
#label:recommend(inheritable)="promoted,alias=推荐阅读,single,boolean" #label:article(inheritable)="promoted,alias=文章,single,boolean" #label:recentUpdate(inheritable)="promoted,alias=动态,single,boolean" #label:announcement(inheritable)="promoted,alias=公告,single,boolean" #label:enableTwikoo(inheritable)="promoted,alias=评论,single,boolean" #label:category(inheritable)="promoted,alias=类别,single,boolean" #label:shareHiddenFromTree(inheritable)="promoted,alias=隐藏,single,boolean" #label:iconClass(inheritable)="promoted,alias=图标,single,text" #label:dateNote(inheritable)="promoted,alias=日期覆盖,single,text" #label:shareAlias(inheritable)="promoted,alias=别名,single,text"
🏷️ 标签速查
日常使用参考(不含 #label: 属性提升语法)。
根笔记标签
| 标签 | 说明 | 默认值 |
|---|---|---|
#isHome=true | 标记为博客主页 | true |
#homeId=xxx | 博客主页笔记 ID | — |
#blogTitle=xxx | 博客标题 | — |
#blogDescription=xxx | 博客副标题 | — |
#appearanceDefaultTheme=xxx | 默认主题 dark/light | dark |
#coverDefaultImage=url | PC 端背景图 | /bg-pc.png |
#coverMobileImage=url | 移动端背景图 | /bg-mobile.png |
#twikooEnabled=true | 启用 Twikoo 评论 | true |
#twikooEnvId=xxx | Twikoo 环境 ID | — |
#twikooVersion=x.y.z | Twikoo CDN 版本 | 1.6.41 |
#footerCopyright=xxx | 页脚版权文字 | — |
#footerIcp=xxx | 工信部备案号 | — |
#footerPolice=xxx | 公安备案号 | — |
#footerPoliceUrl=url | 公安备案链接 | — |
#footerBeianIcon=url | 备案图标路径 | /beian.png |
#siteStartDate=YYYY-MM-DD | 建站日期(页脚"已运行 X 天") | — |
子笔记标签
| 标签 | 说明 |
|---|---|
#recommend=true | 标记为推荐文章,出现在"推荐阅读"模块 |
#article=true | 标记为文章,参与"最新发布"排序 |
#recentUpdate=true | 标记为最近更新,出现在"动态"模块 |
#announcement=true | 标记为公告,出现在首页公告区 |
#enableTwikoo=true | 开启此笔记的评论 |
#category=true | 标记为分类节点,出现在分类树 |
#shareHiddenFromTree=true | 从分类树隐藏此节点 |
#iconClass | 图标 CSS class(如 bx bx-code) |
#shareExternalLink | 笔记作为外部链接跳转(取该标签值作为 URL) |
#dateNote=YYYY-MM-DD | 覆盖排序日期 |
#shareAlias | 笔记 URL 别名 |
#blogDescription | 覆盖当前笔记页面的副标题(若不设则使用根笔记的) |
#color | 笔记标题颜色,分类菜单最近更新也使用此颜色 |
🏗️ 架构
配置流、预处理流程参见上文 架构速览。
主页检测
模板渲染时从当前笔记沿父链向上遍历(最多 50 层),找到第一个带 #isHome=true 的笔记作为主页。不再硬编码任何 noteId。
分类树
以主页为根构建,#category=true 标签标注分类节点,#shareHiddenFromTree=true 控制显隐。由 data.js 的 buildTree 递归 SQL 生成。
📁 文件结构
share/
├── blog.ejs # Trilium EJS 模板——博客入口(源码,可读)
├── blog.min.ejs # ★ 压缩单文件产物(部署用,build-min.js 生成)
├── css/blog.css # 全部博客样式(源码)
└── js/blog.js # 客户端交互脚本(源码)
博客预处理控件/ # 后端预处理脚本(共 3 个)
├── BlogPreprocessRender.js # 编排入口(JSX 渲染面板)
├── data.js # 聚合数据(分类树/关于树/标签/文章/动态/公告/推荐/统计/热力)
└── search.js # 搜索索引
build-min.js # 压缩构建脚本(生成 blog.min.ejs)
压缩部署说明.md # 压缩部署文档
💭 写在最后
zfy 是个人项目,边用边改。如果你也用 TriliumNext,欢迎拿去改造。
- 示例博客:brucevon.space
- GitHub:zfy
可以在以下地方留言交流:
- 💬 博客评论区:brucevon.space/lIQxHnOklH2m
- 🐙 GitHub Issues:github.com/brucevon/zfy/issues
⭐ Star 也是鼓励。