之前在 Obsidian 入门系列文章中,我简单分享过个人主页的搭建思路,随后有读者留言询问具体的实现方法。
坦白说,这个功能并不复杂。核心工作仅包括安装一款插件,以及编写几条 Data view 查询语句。
不过,收到这些留言后我发现,不少用户都有类似的需求。随着笔记数量不断增加,每次打开软件时,面对庞大的信息库,时常会感到无从下手。
今天,我就来详细拆解我的做法,希望能为你提供一个切实可行的参考方案。
一切始于一个真实痛点
先来聊聊我的实际使用场景。
我日常使用 Obsidian 进行写作,工作节奏通常是这样的:启动软件,先快速回顾一下昨天的写作内容,然后切换到对应的项目文件夹,继续推进手头的任务。
这套流程听起来很合理吧?
但问题恰恰卡在了“打开软件”这一步。Obsidian 默认会打开你上次关闭时的文件,这个设计本身没有问题——但它常常让我搞不清楚自己上次浏览到了哪个位置。
更令人困扰的是,如果我仅仅是想“随意浏览一下近期的创作”,那么这个默认行为几乎无法提供任何帮助。
我的核心需求其实很简单:我希望每次打开 Obsidian 时,第一眼就能看到一个全景式的概览——清楚了解最近在写什么、写了多少内容,以及今天是否有值得回顾的笔记。
为了解决这个问题,我找到了 HomePage 插件。它的核心功能只有两个:设定一篇笔记作为启动页,以及在你关闭所有标签页时自动回到这个页面。功能纯粹且没有冗余。安装启用后,我便将 My Homepage.md 设为我的笔记大本营。
安装过程非常简单:直接在社区插件中搜索 “HomePage”,点击安装并启用即可。

接着,在插件设置中指定你想要作为主页的笔记文件即可。你可以指定某个具体的文件,也可以灵活选择其他选项,比如随机笔记、日记或周记。
插件的配置项非常精简,设置完成后即可直接使用。
建议你将“Open on startup”和“Open when empty”两个选项都开启。这样一来,当你启动 Obsidian 或关闭所有标签页时,软件就会自动返回你设定好的主页。

我的主页是如何布局的
我的主页被划分为三个核心板块,每个板块都旨在解决一个特定的问题。

板块一:足迹热力图
主页最醒目的位置是一张热力图,它与 GitHub 上的绿色贡献格子类似,直观地记录了我每天的写作活动。哪一天写了多少字,通过颜色的深浅便能一目了然。
这个热力图是通过 Heatmap 插件结合我自行开发的一个本地插件实现的。它会读取本地的 activity-log.jsonl 文件,将每日写作数据汇总并生成图像。在热力图下方,还附带了关键统计信息:今日最高字数、总字数、以及今年累计写作天数。最后一行则是年份进度:“2026 年已经过去了 X 天”。
坦白讲,我目前的实现方式略显复杂。当初编写时是因为没有更成熟的解决方案,但现在社区中已经有了更好的选择。例如 Activity Heatmap 插件,安装后即可自动追踪文件变动并生成热力图;还有 Activity Graph 插件,功能更加丰富,支持在正文中嵌入,并能追踪 Tasks 插件的完成情况,提供多种视图模式。如果你也想在主页添加热力图,建议直接采用这些现成的插件,操作更省心。
热力图下方还有两行由 Data view 自动计算得出的文字——今年累计写作天数及每日统计。这些完全是机器自动完成的工作,你只需编写一次查询,它便会每天自动更新。
我将所使用的语法整理如下,供你参考。你可以将这段代码和你的具体需求交给 AI,让它帮你生成符合你需求的版本。后面两个区块也同样适用:交给 AI 来优化即可。
今天是`=dateformat(date(today), "yyyy年M月d日")`,`=date(today).year` 年已经过去了`=(date(today)-date(date(today).year + "-01-01")).days`天。data viewjs
// --- 配置 ---
const LOG_FILE_PATH = "_internal/activity-log.jsonl"; // 确认日志文件路径
// --- 主逻辑 ---
(async () => {
let logContent = "";
try {
logContent = await dv.io.load(LOG_FILE_PATH);
} catch (e) {
dv.paragraph("❌ 无法加载活动日志文件。");
console.error("Error loading log file for footprint:", e);
return;
}
if (!logContent || logContent.trim() === "") {
dv.paragraph("- 你今年还没有留下任何足迹。");
return;
}
// 获取当前年份
const currentYear = moment().format('YYYY');
const parsedLogEntries = [];
const lines = logContent.trim().split('\n');
for (const line of lines) {
if (line.trim() === "") continue;
try {
const entry = JSON.parse(line);
if (entry && entry.date && typeof entry.date === 'string' && entry.date.length === 10) {
parsedLogEntries.push(entry);
}
} catch (e) {}
}
const thisYearEntries = parsedLogEntries.filter(entry => entry.date.startsWith(currentYear + '-'));
if (thisYearEntries.length === 0) {
dv.paragraph(`- 你在 ${currentYear} 年还没有留下足迹。`);
return;
}
const uniqueThisYearDates = new Set(thisYearEntries.map(entry => entry.date));
const footprintDaysThisYear = uniqueThisYearDates.size;
dv.paragraph(`- 你在 ${currentYear} 年总共留下了 ${footprintDaysThisYear} 天的足迹,继续加油!`);
})().catch(err => {
dv.paragraph("❌ 计算当年足迹天数时出错: " + err.message);
console.error("Error calculating this year's footprint days:", err);
});
关于 Data view 的更多用法,可以参考我之前的系列文章:
- [[Obsidian 入门22:认识 Data view 插件]]
- [[Obsidian 入门23:别让待办烂在笔记里!用 Data view 自动汇总你所有的 TODO]]
- [[Obsidian 入门24:不会写 Data view 代码?教你一招,让 AI 成为你的专属“程序员”]]
板块二:那年今日
这个区域同样是一个 Data view 查询,它会找出所有创建日期与“今天”一致的笔记,并按时间倒序排列。
这个功能带来的体验非常奇妙:今天打开 Obsidian,你可能会看到“一年前的今天我写了一篇关于 X 的文章”,或者“三年前的今天我刚开始学习 Y”。有时你会惊讶——原来我在那么早的时候就在思考这个问题了。
实现这个功能并不复杂,只需要几行 Data view 代码。但它创造了一种连续的观感:你的笔记库仿佛一条流动的河流,每一天都会收到来自过去的自己的提醒。
## 那年今日
今天是 `=dateformat(date(today), "yyyy年M月d日")`,那年今日,你曾记录过:
```data view
TABLE WITHOUT ID
file.link AS "Article",
dateformat(file.ctime, "yyyy-MM-dd (EEE)") AS "创建时间",
dateformat(file.mtime, "yyyy-MM-dd HH:mm:ss") AS "最后修改时间"
FROM ""
WHERE dateformat(file.ctime, "MM-dd") = dateformat(date(today), "MM-dd")
SORT file.ctime DESC
LIMIT 100
板块三:今日随机推荐
这是我最喜欢的一个小设计。
如果从现有笔记中随机抽取一篇,你每天可能看到任何内容。但问题在于:如果每次刷新都换一篇,那就失去了“今天”的专属感。
因此,我的做法是——基于当天的日期生成一个固定的随机种子。今天打开,看到的是笔记 A;明天打开,看到的则是笔记 B。而在同一天内,无论你刷新多少次,看到的都是同一篇。这就形成了一个有趣的组合:既有稳定性,又保留了偶然发现的乐趣。
有时我会翻到那些自己都快忘记写过的笔记,重新读一遍,感觉还挺有意思的。
data viewjs
const allNotes = dv.pages('""').file
.filter(f => f.path &&
f.path.endsWith(".md") &&
!f.path.includes("Templates/") &&
!f.path.includes("_attachments/"));
dv.header(2, "今日随机推荐");
if (!allNotes || allNotes.length === 0) {
dv.paragraph("*没有找到符合条件的笔记*");
} else {
try {
const today = new Date().toISOString().slice(0, 10);
const seed = parseInt(today.replace(/-/g, ""));
let randomIndex = Math.abs(Math.floor(Math.sin(seed) * allNotes.length)) % allNotes.length;
if (randomIndex < 0 || randomIndex >= allNotes.length) {
randomIndex = 0;
}
const randomNote = allNotes[randomIndex];
if (randomNote && randomNote.path) {
dv.paragraph(`[[${randomNote.name || randomNote.basename || "未命名笔记"}]]`);
const folderPath = randomNote.path.split("/").slice(0, -1).join("/");
dv.paragraph(`*"${folderPath || "根目录"}" 中的笔记*`);
} else {
dv.paragraph("*无法获取随机笔记信息*");
}
} catch (error) {
dv.paragraph(`*生成随机推荐时出错: ${error.message}*`);
console.error("随机笔记生成错误:", error);
}
}
你可以从何处开始
如果你也想在 Obsidian 中搭建个人主页,我的建议是:不必追求一步到位。
最简单的起步版本:安装 HomePage 插件,新建一个笔记,在里面放入你最常用的几个链接,比如“每日笔记”、“项目文件夹”、“正在写的文章”。这样就足够了。
你的主页只有一个核心目标——让你每次打开 Obsidian 时,都能清楚地知道从何处开始。等你习惯了拥有一个主页,再逐步添加更多功能。例如,想查看最近修改了哪些文件?可以增加一个 Data view 的最近修改列表。想统计写作情况?可以再添加一个热力图插件。
不要一开始就想着做到和我一样复杂。请先想清楚一个问题:我每天打开 Obsidian,最想看到的是什么?这个问题的答案,就是你主页应该承载的内容。
说到底,搭建个人主页与技术关联并不大。你不需要精通代码,不需要熟悉 Data view 的语法,甚至不需要使用和我完全相同的插件。你唯一需要想清楚的是——每次打开 Obsidian,你最希望映入眼帘的那个画面是什么。
对我而言,是热力图上逐渐连成片的绿色格子,是“那年今日”里突然跳出的旧笔记。对你来说,那可能是完全不同的东西。
但关键在于:每一次打开 Obsidian,你都能拥有一个熟悉且带有个人印记的起点。
工具本身是次要的。找到最适合自己的方式,才是最重要的。
