3245 字
16 分钟
从 0 搭建 self-built-space:开发、部署与 Obsidian 发布工作流复盘

这篇文章是 self-built-space 的一次工程复盘。我把仓库里的设计文档、开发计划、部署文档、运维手册、Obsidian 工作流文档和 Git 提交记录重新读了一遍,按时间线把这个站点从空目录走到当前状态的过程整理出来。

一开始我想做的并不是“再搭一个博客”。更准确地说,我想要一个能长期放东西的自建空间。学习文章放技术笔记和工程实践,点点滴滴放日常记录,资源页放工具、模板、链接和可复制的本地资料,关于页则负责说明我是谁、我在关注什么。

起点:先弄清楚这个站点要解决什么#

最早的设计文档里,self-built-space 的定位很明确:它要承载内容,也要承载整理动作。这个判断影响了后面几乎所有技术选择。

我没有从零写一套博客系统,而是选了 Fuwari 做底座。它已经有 Astro 静态站、Markdown、标签分类、归档、RSS、Sitemap、Pagefind 搜索、代码块增强和响应式布局。自己要做的部分集中在另外几件事上:

  • 首页的信息结构和视觉识别。
  • 学习、点滴、资源三类内容的区分。
  • 资源页的独立数据模型。
  • 更适合中文阅读的排版。
  • 从写作到发布的长期维护流程。

这个选择挺务实。Fuwari 负责那些已经成熟的博客能力,我把精力放在“这个站点为什么存在”和“以后怎么继续写”上。

Sprint 0:把 Fuwari 项目骨架迁进来#

第一个提交是 a59d758 chore: init Fuwari project skeleton。这一阶段做的是项目启动。

当时工作区里已经有 docs/ 文档,所以开发计划里没有建议直接在当前目录初始化项目。更稳的做法是先在临时目录生成 Fuwari 项目,确认结构和依赖没问题,再迁移到当前仓库。这样不会把已有设计文档冲掉。

迁移完成后,仓库里有了这些基础能力:

  • src/pages/ 路由。
  • src/content/ 内容集合。
  • 文章详情页、归档页、RSS、搜索组件。
  • pnpm devpnpm build 等命令。
  • Astro 的静态构建输出。

这个阶段看起来只是“把模板放进来”,但它给后续所有定制提供了一个可以跑、可以构建、可以回滚的起点。没有这个起点,后面的内容模型和视觉调整都会变成一堆散落的实验。

Sprint 1:先把模板改成自己的站点#

第二个提交是 ae5b662 chore: sprint 1 - 基础配置。这一阶段处理站点身份。

主要改动包括:

  • 站点标题改成 Icicn。
  • 副标题改成「学习 · 记录 · 整理」。
  • 语言切到 zh_CN
  • 导航改成:首页、学习、点点滴滴、资源、关于我。
  • 作者昵称、社交链接和站点地址换成自己的配置。

到这里,站点还没有自己的复杂页面,但已经不是默认模板了。导航名称、语言、作者信息和基础 SEO 都指向同一个内容方向:这是一个中文个人站,而不是 Fuwari 的演示项目。

Sprint 2:先定内容模型#

第三个提交是 5515672 feat: sprint 2 - 内容模型。这一步很影响后面的结构。

文章没有被拆成两套 collection。学习文章和点滴文章仍然共用 posts,只是在 frontmatter 里增加了一个 kind 字段:

kind: learning

或者:

kind: moments

这样做的好处很直接。文章详情页、Markdown 渲染、RSS、搜索、标签、分类、目录和代码块都能继续复用。列表页只需要按 kind 过滤。

资源则单独建了 resources collection。资源卡片和文章不一样,它更像一个索引条目,需要 titletypeurldescriptioncategorytagsfeaturedupdated 这些字段。把它单独建模,比硬塞进文章系统里更干净。

这个阶段也加入了学习、点滴和资源示例数据,方便后面做页面时有真实数据可以跑。

Sprint 3:把核心页面串起来#

第四个提交是 41431ec feat: sprint 3 - 页面结构。这一阶段把设计文档里的核心路由落到了代码里。

新增或调整的页面包括:

  • /:首页,负责个人定位和内容分发。
  • /learning/:只显示 kind: learning 的文章。
  • /moments/:只显示 kind: moments 的文章。
  • /resources/:读取资源 collection。
  • /about/:展示个人介绍。

content-utils.ts 也在这个阶段变得更有用。它开始负责按 kind 取文章、按 featured 和更新时间排资源。页面本身不用关心太多内容读取细节。

这一阶段完成后,站点终于有了清楚的信息架构。学习和点滴不再只是导航上的两个词,资源页也不再只是一个未来计划。

Sprint 4:首页视觉和中文阅读体验#

第五个大阶段是 ef78326 feat: sprint 4 - landing page visual customization complete。这一步给站点加上了更强的视觉记忆点。

参考 Dala 和 Refero 风格后,首页选择了暗色画布、紫色强调和粒子宇宙的路线。技术上引入了 Three.js,包括:

  • 旋涡星系粒子场。
  • 鼠标视差和粒子扰动。
  • 星空背景和闪烁动画。
  • shader 与 Three.js 场景代码。
  • / landing page 和 /home/ 内容聚合页的双页面结构。

Dala 的原始技术路线很复杂,里面有 GLB 实例粒子、EXR 数据贴图、FBO/GPGPU 模拟和滚动驱动。self-built-space 第一版没有照搬这些。它先用程序生成粒子和 Three.js 场景做出氛围,把实现复杂度压在可维护范围内。

后面几个提交继续调整站点气质:

  • 62bec48 精简 /home/,让内容区更快进入正文。
  • f4467f7 修复 Astro check 报错。
  • 25ba7bd 打磨关于页。
  • 6e440e1 调整字体系统和中文排版。
  • 8b13e9c 增加沉浸式主题。
  • 76bfe50 增加首页轮播 banner。
  • ceed658 改善内容页可读性。

这里我比较喜欢的一点是,首页可以有强视觉,但文章页没有跟着变得浮夸。正文页一直在往可读性靠,字号、行高、卡片边界和移动端布局都比“炫”更优先。

资源系统:从外链列表到本地资源合集#

资源页最开始只是外部链接卡片。后来 8afda30 feat: add local resource collections 把资源系统扩了一层。

新增的模型有两个:

  • resourceCollections:资源合集,比如提示词模板、代码片段。
  • localResources:具体条目,通过 collection 绑定到某个合集。

页面上也增加了 /resources/<slug>/,用来展示某个本地资源合集。LocalResourceGallery.svelte 负责卡片、弹窗和详情展示,ResourceCopyButton.svelte 负责复制资源主体内容。

这样一来,资源页就不只是“我收藏了一些链接”。它可以放真正能复用的东西,比如代码片段、提示词、配置模板、素材说明。外部资源、本地合集、本地条目各自有自己的模型,后面维护起来也不会混在一起。

搜索、RSS 和项目文档#

上线准备阶段主要看两个提交。

e17bcf1 feat: sprint 5 - prepare production deployment 加入了 GitHub Actions 部署 workflow,调整 schema,更新 favicon,也处理了 RSS、资源页、首页和搜索相关细节。

2422ebe feat: refine search architecture and project docs 重构了搜索相关代码,新增 src/lib/search/site-search.ts,同时整理 README,移除一些不再需要的默认项目文档和导航逻辑。

搜索使用 Pagefind。构建命令实际会做两步:

Terminal window
astro build && pagefind --site dist

所以搜索不能只靠 pnpm dev 判断。要看真实搜索索引,需要先构建,再用 pnpm preview 检查。这个提醒后来也写进了维护文档。

部署:让服务器只做服务器该做的事#

部署文档和运维文档最后确定了发布方式:CI 构建,加 SSH/rsync 发布。

整体流程是:

本地修改内容或代码
-> git commit
-> git push
-> GitHub Actions 安装依赖并构建
-> rsync 上传 dist/
-> 服务器 release 目录
-> current 软链接切换
-> Nginx 对外提供静态站点

这里有几个取舍我觉得是对的。

第一,Git 仓库是源码、内容和配置的来源。服务器上不直接改文章,也不直接改 dist/

第二,CI 负责安装依赖、检查、构建和生成搜索索引。服务器不承担构建任务,只托管静态文件。

第三,服务器目录用 releases/current 软链接。每次部署都是一个独立 release,出问题时可以把 current 指回旧版本。

第四,部署用户是单独的 deploy 用户,不用 root。SSH key、GitHub Secrets、Nginx root、release 清理这些细节都在部署文档里有记录。

GitHub Actions workflow 位于 .github/workflows/deploy.yml。它在 push 到 main 时触发,也支持手动 workflow_dispatch。CI 会依次执行 pnpm checkpnpm type-checkpnpm build,然后用 rsync 上传 dist/,切换服务器上的 current,最后保留最近 5 个 release。

启用后的日常维护#

站点启用后,维护手册把日常流程写得很朴素:

Terminal window
pnpm check
pnpm type-check
pnpm build
git status
git add <files>
git commit -m "<type>: <summary>"
git push

如果改到了视觉、搜索、首页动效或移动端布局,再额外跑:

Terminal window
pnpm preview

内容目录也固定下来:

src/content/posts/ 学习文章和点滴文章
src/content/resources/ 外部资源条目
src/content/resourceCollections/ 本地资源集合
src/content/localResources/ 本地资源内容
src/content/spec/about.md 关于页内容

这个阶段的重点不是多一个命令,而是形成习惯:本地验证,提交,推送,CI 部署,线上验收。内容更新和代码更新走同一条路径。

Obsidian:把写作入口移到更舒服的地方#

最近两个提交把 Obsidian 工作流接了进来:

  • f372731 feat: add obsidian content workflow
  • 0985b65 docs: update obsidian workflow readme

这个方案没有让 Astro 直接读取 Obsidian vault。它加了一个同步脚本,把 Obsidian 当写作源,把网站仓库当发布源。

默认 vault 是:

/Volumes/raw/knowledge/self-built

发布目录约定为:

posts/learning/ 学习文章
posts/moments/ 点点滴滴文章
resources/external/ 外部资源卡片
resources/collections/ 本地资源合集
resources/local/ 本地资源条目
_attachments/ 共享附件
templates/ Obsidian 写作模板

同步脚本是:

scripts/sync-content-from-obsidian.js

常用命令是:

Terminal window
pnpm content:sync
pnpm content:publish-check

同步关系是:

posts/learning/ -> src/content/posts/<slug>/index.md
posts/moments/ -> src/content/posts/<slug>/index.md
resources/external/ -> src/content/resources/<slug>.md
resources/collections/ -> src/content/resourceCollections/<slug>.md
resources/local/ -> src/content/localResources/<slug>.md

脚本还处理了一些 Obsidian 和 Astro 之间的小差异:

  • 学习文章自动写入 kind: learning
  • 点滴文章自动写入 kind: moments
  • 通过 frontmatter slug 控制输出路径。
  • 支持 Obsidian 图片语法 ![[image.png]]
  • 本地附件会复制到网站仓库。
  • 远程图片和普通外链保持原样。

这层同步适配很值得保留。直接让网站读 vault 听起来更省事,但 CI、路径、附件、草稿和私有笔记都会变麻烦。现在的方式更清楚:Obsidian 负责写,网站仓库负责发布。

回头看,这条路线学到了什么#

第一,内容模型要尽早定下来。kind 字段和资源 collection 很早就出现,所以后面的学习页、点滴页、资源页都有稳定的数据来源。

第二,首页可以做得有记忆点,正文页要收着点。粒子宇宙适合放在入口,长文阅读还是要靠字号、行高、间距和清晰的层级。

第三,部署和内容发布最好走同一条路。这个项目没有单独搞一个“只发文章”的临时流程。文章、资源、配置、页面修改都经过 Git、CI、构建和部署。

第四,资源值得单独建模。链接、合集、可复制条目不是同一种东西。分开之后,资源页才有继续扩展的空间。

第五,Obsidian 工作流真正解决的是写作摩擦。同步脚本把 frontmatter、附件、图片语法和输出目录这些琐碎问题藏起来,写文章时就不用一直切回 src/content/ 里手动对齐格式。

当前状态#

到目前为止,self-built-space 已经能支撑一条完整的内容生产链路:

  • 用 Astro/Fuwari 承载文章、资源、RSS、Sitemap 和搜索。
  • 用自定义页面区分首页、学习、点滴、资源和关于页。
  • 用 Three.js 给首页提供视觉识别。
  • 用 GitHub Actions 构建并发布到服务器。
  • 用 release 目录和 current 软链接保留回滚能力。
  • 用 Obsidian 写作,再通过 pnpm content:sync 同步到网站内容仓库。
  • pnpm content:publish-check 在本地一次跑完同步、诊断、类型检查和构建。

这个项目现在最有价值的部分,不是某个单独页面,也不是某个视觉效果。它更像一条已经接起来的管线:想法先进 Obsidian,发布内容进入 Astro,Git 记录每次变化,CI 负责构建和部署,服务器只安静地托管静态结果。这个状态不华丽,但很适合长期写下去。

从 0 搭建 self-built-space:开发、部署与 Obsidian 发布工作流复盘
https://uuicicn.top/posts/self-built-space-development-history/
作者
Icicn
发布于
2026-06-25
许可协议
CC BY-NC-SA 4.0