Logo

site iconoldj | 老杰

男,80 后,杭州。阿里 8 年,目前在一个小而美的团队。「妙笔 / WonderPen」「ccReader 」作者。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

oldj | 老杰 RSS 预览

WonderPen 存储格式迁移历程

2026-08-18 19:47:36

最近几年我一直在开发写作软件 WonderPen,去年我对 WonderPen 的底层数据存储格式做了一个重大重构,将原本使用 JSON 存储的数据迁移到了 SQLite 数据库。这儿回顾记录一下相关要点。

原本的方案

WonderPen 是一款写作应用,用户可以使用它写文章或笔记,以下统称为文档,一个非常底层的基础需求是如何存储这些文档。

在最早的设计中 WonderPen 功能不多,为了让系统尽可能简单以便尽早发布,我采用了 Markdown 文件来存储文档,每个文档就对应硬盘上的一个 Markdown 文件。

这个方案非常直观,也非常有效,不过,一段时间之后,我发现单纯的 Markdown 只适合记录文档内容,但真实写作场景中,每个文档还有很多额外信息需要记下来,比如基本的添加、修改时间、版本号,以及一些 WonderPen 特色功能(比如每个文档都有对应的备注、写作目标等等)。

虽然这些信息也可以使用 Markdown 头信息或自定义扩展格式来记录,但扩展和处理起来终究有一些不便。再加上 WonderPen 的设计目标并不是做一个 Markdown 编辑器,只是“刚好”选择了 Markdown 格式,且这些文档的原始文件都存储在被称为“文档库”的文件夹中,并不向用户直接暴露。于是,我索性激进一些,将文档的存储格式改为了 JSON 文件格式,一个文档对应一个 JSON 文件,同时有一些专门的 JSON 文件用于记录各种关系数据和额外的属性信息。

之后的几年里,这套系统运行良好,为了管理好这些 JSON 文件,我甚至还专门开发了一个简单的基于 JSON 文件的本地文本数据库系统。

这个方案还有一个额外的好处:用户可以直接把文档库建在 OneDrive、Dropbox 等云盘内,使用第三方云盘来同步数据。虽然偶尔也会遇到云同步时的文件冲突,但总体来说问题不大。

为什么要迁移?

这个方案在初期确实很好,不过,随着软件的不断迭代,使用 JSON 存储数据的缺点也逐渐暴露了出来。

WonderPen 的主要使用场景之一是写长篇著作,比如一部有数百个章节的书籍,或者网络小说。在这个场景中,章节之间并不是独立的,首先章节天然有先后顺序,且这个顺序不一定和文件名的顺序一样,因此除了存储文档的 JSON 之外,还需要一些 JSON 文件来存储章节目录顺序等元信息。

同时,一些新的需求和场景也在不断出现,比如对全库进行搜索、替换等操作,以及快速读取各个章节的备注内容等操作。如果每个文档对应硬盘上的一个 JSON 文件,当文档数量很多时,搜索、批量替换等操作意味着要在短时间内对大量的小文件进行读写,这个操作是复杂且低效的。

一个方案是在每次启动时把整个文档库的所有数据读入内存,之后所有的操作都在内存中处理,并定期写回硬盘。这确实是一个可行的方案,我曾经认真考虑过,且对写作应用来说,用户作品的主要内容是纯文本,即使数百万字的文档库,加上各种额外数据,理论上也不过占用十几兆至几十兆内存,对现代设备来说不会有太大的压力。

不过我最后还是没有采用这个方案,因为深入评估之后,我发现要让一些操作尽可能高效,我需要引入不少复杂的算法,如果沿着这条路走下去,我实际上是在实现一个简化但基本功能都有的内存数据库系统。

既然这样,为什么不直接使用已有的成熟数据库呢?

文档库中的数据是高度结构化的,且用户并不会直接访问这些原始数据,要进一步高效地处理这些数据,使用数据库就成了自然的选择。

使用 SQLite

定下方向之后,接下来就简单多了。

现在可供客户端(包括桌面端和移动端)使用的数据库有不少方案,有一些还自带了云同步的功能。不过综合比较之后,我还是选择了最成熟的 SQLite。

接下来就是客户端的改造了。WonderPen 桌面端基于 Electron,下面重点介绍一下桌面端的处理。

在 Node.js 22.5 之前(对应 Electron 35 之前),Node.js 没有内置 SQLite 支持,因此在 Electron 中使用 SQLite 需要安装第三方库,比较知名的有 sqlite3、better-sqlite3、libsql 等,其中比较推荐的是 better-sqlite3 和 libsql。

不过 Node.js 22.5(对应 Electron 35)开始内置了 SQLite 支持,如果你在使用 Electron 35 或更新的版本,可以考虑直接使用 Node.js 内置的 SQLite 支持。

WonderPen 刚开始向 SQLite 迁移时,Electron 35 还没有发布,因此只能使用第三方库。由于 better-sqlite3 在性能等测试上表现优异,我便也选择了它。

不过,由于 better-sqlite3 包含原生 Node addon(.node 文件),随之而来的缺点是在 Electron 打包时需要编译一些原生模块,可能会遇到一些环境或配置上的麻烦。好在这些麻烦是一次性的,加上现在 AI 已经足够聪明,遇到问题问一下 AI 基本就能搞定,只需搞定一次配置,后续升级、打包时都可以复用。

libsql 原本是 Turso 这家公司为自己的 SQLite 服务写的库,可以访问由 Turso 提供的远程 SQLite 服务,但它并没有和 Turso 强绑定,也可以用作一个通用的访问本地 SQLite 的库。

有评测表示 libsql 的性能比 better-sqlite3 差一些,约 20% 这样的数量级,但对大多数桌面或移动端应用来说这一点性能损失影响很小,因为多数应用的性能瓶颈本并不在数据库上。

和 better-sqlite3 相比,libsql 内置了所需的二进制文件,不需要额外编译,因此在 Electron 中打包时非常方便,就和安装普通 npm 包一样。

你可以根据自己的实际情况,决定选择 better-sqlite3 还是 libsql。

在 WonderPen 项目中,我曾经同时使用了 better-sqlite3 和 libsql,正式项目中使用的是 better-sqlite3,以获得更好的性能,但在测试用例中使用了 libsql,以简化配置。因为测试环境是纯 Node.js 环境,和 Electron 环境的 ABI 不同,如果两边都使用 better-sqlite3,就需要在同一个库中安装和编译两个不同的版本,比较麻烦。

另外,如果性能要求不是很高,也可以直接在正式项目中也使用 libsql,配置会简单很多。不过在写作本文时,如果你在使用 Electron 35 或更高的版本,最简单的方案是直接使用 Node.js 内置的 SQLite 库。

目前,WonderPen 也已经从 better-sqlite3 和 libsql 迁移到了 Node.js 内置的 SQLite 库。虽然内置的 SQLite 库现在还被标记为“Release candidate”,但实际使用下来已经足够稳定,基本没有遇到问题。

移动端

WonderPen 的移动端一开始是基于 Flutter 的,后来迁移到了 Capacitor.js。和桌面端一样,移动端也使用了 SQLite。

我在桌面端和移动端之间共享了数据库迁移文件,确保两端的数据库结构完全一样。这样的好处是只需要维护一套数据结构,开发时心智负担会小很多,且一些代码和流程可以直接复用。理论上,两端的数据库文件也可以直接复用。

ORM

WonderPen 没有使用传统的 ORM 库,但也没有直接拼 SQL 语句,桌面端最早使用了 Knex.js,后来改用了 Drizzle.js。

改用 Drizzle.js 的原因是它能很容易地同时运行在 Electron 和 Capacitor.js 环境,而且它支持 Proxy,意味着你可以自己抽象一层代理,无论底层运行的是 better-sqlite3、Node.js SQLite 库还是 Capacitor.js 的 SQLite 库,都可以使用 Drizzle.js 包装一层,对外使用一样的接口。这样,Electron 端和 Capacitor.js 端涉及数据库操作的业务代码可以完全共享,保证关键逻辑一致。

其他

当然,从文件存储迁移到 SQLite 也不是完全没有代价的,最大的问题是如果数据库文件损坏,可能所有文档都无法再正常读取。

SQLite 本身足够强健,正常使用一般不会出现文件损坏。但一些用户可能会将数据库放到 OneDrive 等云盘中,使用第三方云盘在不同设备之间同步数据。WonderPen 在使用时对数据库的读写很频繁,每次文档修改都可能会触发云盘同步,如果 SQLite 文件比较大或网络比较慢,经常会发生上次同步还没完成文件又被修改了的情况,有时候,云盘在处理同步时可能会出错,导致 SQLite 文件损坏。

以前使用 JSON 文件存储数据时这个问题不明显,因为即使同步出错也最多影响那一个文件,但 SQLite 数据库文件本身是一个大的二进制文件,一旦出错,有一定概率损坏数据库的格式,导致这个数据库文件后续无法正常读写。

简单来说,OneDrive 等云盘不适合存储比较大且会频繁变化的二进制文件。

好在这种情况一般只在将 SQLite 数据库文件放在云盘中同步时才会发生,如果是放在普通盘,基本不会遇到什么问题。

小结

如果应用的数据量比较小,且没有复杂的查询需求,使用文本文件或 JSON 文件存储数据是简单可行的方案。不过,如果数据比较多,或者开始有复杂的数据关系或查询需求时,应该考虑使用数据库方案。

对本地应用来说,SQLite 数据库是不错的选择。如果有桌面端、移动端等不同的版本,可尽量保证底层数据结构一样,以便共享数据结构以及操作相关的代码,减小工作量。

挖井

2026-07-19 11:11:15

AI 时代,软件开发的门槛大大降低了。这两年经常看到有人说,用 AI 每个月 vibe 一个 App 出来,不成功就换一个,做上 n 个总会有成功的。

这个说法让我想起来以前看过的一幅漫画:一个人挖井,他在很多地方都挖了坑,但每个都不够深,到最后也没挖出水来。

那些不停地做新 App,希望以量取胜并期待哪天有一个突然成功的做法,和漫画中人物的做法很类似。

不排除有人运气很好,刚好在一个地下水很浅的地方挖掘,于是很容易就挖出水了。但这种情况下,你能在这儿挖,别人也能来这儿挖(跟风/抄袭),很快这块地就会被挖得乱七八糟。

也不排除有人天赋异禀,一铲能顶普通人十铲,随便挖了一会儿就挖得足够深,也挖到了水。但这样的人毕竟是少数,也许是千里挑一甚至万里挑一。

更常见的情况,是一位天赋相对普通的人,来到一块潜力也相对普通的地方,尝试挖一口井。此时,只有坚持不懈,在一个位置挖得足够深,才有可能挖出水来。

做产品也是类似。

确实存在一些看似简单的 App 突然爆火,但它们的成功通常有很大的运气成份,难以复制,甚至作者自己的下一个产品也未必能继续有这样的热度。而大部分稍微有点复杂度的产品,要想做得足够好,都需要投入大量时间和精力,一两个月根本不够,即使有 AI 加速,投入的时间往往也需要以半年甚至年来计算。

当然,把时间全押在一个项目上,也确实是有风险的,万一方向错误,投入的时间真的就全打水漂了。

一个折衷的方案是:把时间分成两份,一份坚持长期主义,专注于一个项目,不断打磨完善它;另一份则采用探索模式,可以不断尝试新项目,也许哪天会有意外的收获。

SwitchHosts 5.0

2026-05-26 10:40:32

一转眼,SwitchHosts 已经存在了 15 年了,距离上一个大版本更新也过去 5 年了。最近抽空把 SwitchHosts 升级到了 5.0,最大的变化是将底层从 Electron 换成了 Tauri,安装包的体积小了很多。

这个过程的大部分工作都是用 AI 完成的,在这儿记录一下。

动机

这次升级的主要动机是想解决 SwitchHosts 体积过大的问题。

之前的版本是基于 Electron 实现的,打包后安装文件有大几十兆,且随着 Electron 版本的提升,这个体积还在不断变大,因为它内部依赖的 Chromium 等在不断变大。

Electron 是一个很好的框架,我还有一些其他项目也是基于 Electron 实现的,我很喜欢它。但 SwitchHosts 只是一个小工具,功能也很简单,几十兆的安装包对它来说有点重了,在 GitHub 的 issues 中也经常有人诟病这一点。

在这个版本之前我已经有很长一段时间没有对 SwitchHosts 做实质性的更新,原因之一就是考虑到后续可能要换架构,因此不太想再在原来的代码上做太多改动。

还有一个升级的动机则有点神奇:去年年底,SwitchHosts 收到了一笔来自 Warp 的赞助。虽然金额不多,却也是一个意外之喜了。

既然还有人在关注和使用 SwitchHosts,我想我也应该继续完善它,于是,就有了 v5 这个版本。

方案

SwitchHosts 需要支持 Windows、macOS、Linux 三个平台,我没有足够的精力同时维护三套代码,因此首选还是跨平台技术或者框架。

考虑到要尽可能复用之前的 UI,技术方案上还是需要选择 WebView 界面或类似的方案,因此最终可用的选项并不是太多,其中最为成熟的就是 Tauri 了。

我也考察过一些其他方案,其中 Wails 看起来不错,也有一些成功案例,不过它的生态似乎还是小了一些,同时它的 v3 还处在 Alpha 阶段,用在正式产品中有一定风险,如果我先用 v2 开发,可能不久之后又要再做一次迁移升级到 v3。

新出现的 Electrobun 也很有吸引力,它最大的优点是类似 Electron 可以使用 JS/TS 来写后台代码,不像 Tauri/Wails 那样需要使用 Rust/Go。不过它才发布没多久,看评价可能还有不少奇怪的 bug 或适配问题。

Tauri 当然也不完美,对前端来说最大的难点是一旦深入或者要做什么定制就要和 Rust 打交道,它打包体积小的代价是不同平台上可能会有不同的表现,推特上就有不少使用 Tauri 遇到大坑然后不得不放弃的经历分享。

不过,考虑到 SwitchHosts 只是一个功能比较简单的小工具,不会涉及太多复杂的平台相关的操作,在考察一圈之后,我最终还是选择了 Tauri。

过程

这次升级,大致上有两个阶段,分别是界面升级和架构升级。

界面升级

在投入 Tauri 之前,我先发了一个 v4.3.0 版,这个版本仍然是 Electron 架构,唯一的目标是先把 UI 框架从 Chakra 升级到 Mantine,主要原因是我在其他项目中大量使用 Mantine,对它更熟悉,也更喜欢它的设计风格。

架构升级

第二阶段则是重头戏,从 Electron 迁移到 Tauri。

迁移的工作基本都是由 AI (Claude Opus 4.6/4.7 + GPT-5.5)完成的,在动手之前我和 AI 聊了很久,先让它理解这个项目,然后评估迁移到 Tauri 的可行性,以及可能会遇到什么问题。

大体上的方案就是界面部分基本不动,但原本 Electron 的 main 进程的逻辑迁移到 Rust 实现。虽然理论上大部分工作可以放到渲染进程中用 JS/TS 完成,后台只负责基础的数据读写等工作以尽量避免写 Rust,但有 AI 的帮助,我还是选择了把所有后台逻辑(原 main 进程的逻辑)转为了 Rust。

当把所有能想到的问题都考虑到,确认迁移能顺利完成之后,我再让 AI 根据结论,制定一个详细的迁移计划,包括分成几个步骤,每步骤要做什么,验收标准是什么,然后将这些步骤保存为多个 Markdown 文档。

接着,我人工检查了这些计划文档,继续向 AI 提问、修改,直到我自己看不出明显问题,然后交给 AI,让 AI 自己反复审核这些计划文档,寻找可能的风险点以及没有考虑周全的地方,直到 Claude Opus 基本没有发现问题,又让 GPT 再检查了几遍,确保没有明显的疏漏。

计划确定后,就是执行的步骤了。这个过程很简单,让 AI 读取计划文档,一步一步地执行,每完成一个步骤,就将进展写入一个专门的 progress.md 文件,这样一方面方便自己随时检查,另一方面也是为了在 AI 流程中断或者新开对话的时候能快速继续工作。

大概是前期计划比较充分,加上现在 AI 确实已经足够智能,迁移工作大体上很顺利,断断续续地搞了几天,核心迁移就基本完成,新的 Tauri 版能跑起来了。

再接下来,就是一些琐碎的工作,各种细节的处理,新版本 UI 的改造,一些小功能的调整,以及反复检查是否存在 bug 或潜在问题。相对于迁移来说这部分的难度小了很多,但耗时却更长一些。

成果

最终,Tauri 版打包后的安装文件只有数兆,其中最小的 Windows 版只有 2.7M,较大的 macOS 通用版也不过 7.6M,体积是 Electron 版的几十分之一。

不过,在运行的时候,Tauri 版仍然会占用一百至数百兆的内存,比 Electron 版略少一些,但不像安装包那样有数量级的变化。这些内存基本上是由 WebView 进程使用的,因为 Tauri 的界面本质上是 Web 页面,除非彻底换成原生方案,不然大概没有特别好的办法来进一步降低内存占用。

现在从体积上来说 SwitchHosts 是一个小工具了。后续我会在保持轻量的前提下,继续完善它。

小结

最近几年 AI 的发展可谓日新月异,即使是在一两年之前,我也无法想象仅靠 Vibe Coding 的方式就能完成如此复杂的工作。

这个迁移很复杂,工作量也很大,且我对 Rust 并不熟悉,如果没有 AI 的帮助,我不会有勇气采用这么激进的迁移方案,即使采用,耗时可能也会是现在的数倍甚至十倍。AI 已经深刻改变了程序员的工作方式。

使用 AI 进行复杂工作是可能的,但要注意先制定好详细的计划,并将这些计划按阶段写成文档,然后让 AI 一步一步地执行。因为 AI 的上下文窗口大小有限,如果不将计划写下来就直接开始,AI 很可能执行到后面就忘了前面。

最后,喜欢各位新老用户喜欢 SwitchHosts 的新版本。❤️

开源本站博客系统 - Publa

2026-05-12 11:24:59

自从 2010 年注册了域名 oldj.net 并架设了这个博客,一转眼已经 16 年了,这么长的时间足够发生很多事,比如杨过和小龙女都已经再次重逢了,本站也经历过了 N 次重构甚至推倒重来。最近,在 AI 的帮助之下,我重写了站点的后台,并决定将这个博客系统开源。

历程

这个博客最早的版本是使用 Django 写的,后台使用了 Django 自带的 admin,前台则使用 Django 的模板输出 HTML。

一段时间之后,我有点懒得继续改进,便迁移到了 WordPress。不得不说,WordPress 的功能非常完善,插件也很多,使用起来非常方便。

再后来,我担心自己维护的 WordPress 有安全问题,便一度将站点迁移到了 WordPress.com 官方服务。

不过,又一段时间后,我发现官方服务有不少限制,比如想在文章中插入数学公式需要升级高级会员,成本实在有点高,加上官方服务位于海外,访问总是有一点慢,于是又迁回了自已部署的 WordPress。

但很快,我发现使用 WordPress 时,如果启用了某些功能或者某些插件,站点仍然需要从海外下载文件,整个博客的访问速度也因此被拖慢,于是又起了自建的心思,花了一些时间,用 egg.js 重写了博客系统。

不过 egg.js 本质上来说仍是上一代框架,或者说是基于后端视角开发的框架,它很适合写后台服务或者传统网站,但对 React 生态的支持则几乎没有。此时,React 等前端框架的边界不断拓展,除了写前端代码,还出现了 Next.js 这样的框架,让它能在服务端渲染 React 组件,解决了之前 React 站点对搜索引擎不友好的问题。

于是,我又开始折腾,用 Next.js 重写了这个博客站点。但这次我只用 Next.js 写了博客的前台页面,后台则改为继续使用 Django,Next.js 通过 API 向 Django 读取数据并渲染。

随着时间的流逝,我又逐渐厌倦了在 Django Admin 中写博客的体验,一直计划着再次改进这个博客,只是一直没抽出时间。

转眼到了 2026 年,AI 的发展如火如荼,Codex、Claude 等模型在写代码方面的表现已经非常出色,也深刻地改变了我日常写代码的方式。我决定花一些时间,在 AI 的帮助下完成这个博客系统后台的改造。

于是,就有了现在这个博客系统,我叫它 Publa(读音:/ˈpʌb.la/,帕布啦)。

设计理念

Publa 不是一个大而全的博客系统,它的目标用户是个人或小团队。在设计和开发过程中,我一直秉承着以下设计理念:

轻量

不要做得太重太复杂,要让部署尽可能简单,比如可以直接在 Vercel 上部署,或者只需要一个 Docker 文件就能部署。

动态

现在有很多静态博客系统,它们使用 Markdown 编写内容,最后生成静态文件并发布上线。

这些博客系统都很不错,且因为是纯静态,运行和维护的成本极低。但缺点也在于纯静态,比如作为静态站点,它们天然不支持评论,如果要让访客发布评论,通常还需要使用第三方工具,无论是体验还是数据,都比较割裂。

如果说评论还能用第三方工具搞定,另外一些比如定时发布、历史记录、站内搜索等等功能,静态博客系统就基本没有办法了。

我希望这个新的博客系统自身就能支持评论、留言、定时发布、站内搜索等功能,因此做成动态便是更好的选择。

易于自定义

Publa 内置了浅色和深色两个主题,同时支持自建主题,或者添加自定义 CSS。

同时,在后台设置中,还开放了自定义 HTML 片断的功能,支持在 <head>末尾、 <body> 开头或末尾等位置插入自定义 HTML。

因此,理论上来说,你可以根据偏好,自定义 Publa 博客前台页面的所有外观样式。

易于迁移

Publa 博客的数据可以一键导出为 JSON 文件,你也可以根据这个格式规范,将其他博客的内容转为 JSON 并导入 Publa。

这个导出功能,也可以用作站点的数据备份。

所见即所得编辑器

Markdown 是一项伟大的技术,我也用 Markdown 写了多年的博客和各类文档。不过,当我开始编写 Publa 后台时,我还是添加了一个基于 TipTap 的所见即所得编辑器。我已经用这个编辑器写了几篇文章,目前感觉良好,尤其是在要插入图片等场景,所见即所得编辑器会更加直观易用。

当然,如果你偏好手写 Markdown,或者想发布已经用 Markdown 格式写好的内容,Publa 后台也支持直接输入 Markdown。

顺便,Publa 的编辑器支持内容自动保存,写作的时候,每隔 5 秒会将编辑器中的内容保存到 localStorage,每隔 30 秒会自动将内容保存到云端草稿,如果连续三次自动保存失败(比如网络中断)会在顶部显示通知;同时,后台还会定期为变化的内容保存历史版本,后续如有需要可随时查看或恢复到之前的版本。基本上,你可以放心地在 Publa 后台撰写文章,而不用担心内容丢失。

支持添加自定义页面

除了博客文章,有时我们还需要添加一些自定义页面,比如「关于」页面。

在 Publa 中,你可以任意添加自定义页面,并指定访问路径,只要这个路径与已有页面不重复即可。自定义页面支持复用当前页面框架(页头、页脚),可用于快速添加一些常见的功能页面,也支持不使用任何模板,由你直接输入整个页面的 HTML,添加完全自定义的页面。

一些特色

除了以上设计理念外,Publa 还有一些值得提一下的特色功能。

支持附件上传

写博客少不了配图,有时可能还要向用户提供一些文件以供下载。Publa 内置支持 AWS S3、Cloudflare R2、阿里云 OSS、腾讯云 COS,如果你有这些平台的账号,可在 Publa 后台添加配置,之后便可以直接在附件管理页面上传和查看附件了。也可以直接在编辑器中粘贴图片,Publa 会自动将图片上传到你配置的平台。

Publa 会管理通过它上传的文件,如果你在 Publa 后台删除了文件,对应的存储平台上的文件也会同时删除。

一键将图片设为 1/2 尺寸

这是我之前写博客时的一个痛点。

随着高分屏的普及,我们给屏幕截图时,得到的图片尺寸通常是截图区域的两倍大小,插入到博客中时,我们希望它能以实际尺寸的 1/2 大小进行显示。

在大多数富文本编辑器中,作者只能拖拽调整图片尺寸,然后凭感觉调一个差不多的尺寸。或者在 Markdown 编辑器中,先查看一下原图的尺寸,再人工计算一下,输入一个 1/2 的宽度。

在 Publa 的编辑器中,我专门优化了这个流程,插入图片后,点击选中图片,图片上方便会浮现一个快捷工具栏,其中便有一个将图片一键设为 1/2 尺寸的按钮。如果你使用高分屏并经常要在文章中插入屏幕截图,那么这个功能对你应该会很有用。

支持添加自定义跳转

在 Publa 中,你可以添加自定义跳转规则,将任意访问路径跳转到指定的路径。

如果你之前在使用其他博客系统,它的文章的路径结构和 Publa 不同,那么你可能会需要这个功能,用于将文章的老路径跳转到新路径,以便用户访问老路径时能正确跳到新的页面。

比如老博客系统中文章路径是 /article/{slug} 这样的形式,但 Publa 中文章路径是 /posts/{slug} ,那么便可以设置一条类似下图的跳转规则,以便平滑迁移。

支持 SQLite 和 PostgreSQL

既然是动态博客,后台数据库自然少不了。Publa 同时支持 SQLite 和 PostgreSQL 两种数据库,你可以根据需要选择。

由于支持 SQLite,如果你想开发调试 Publa 非常简单,只需从 GitHub 上下载源码,安装依赖,然后使用 npm run dev 即可启动测试服务器,在没有指定数据库的情况下,Publa 会自动创建并使用本地 SQLite 以便尽快开始开发调试工作。

值得一提的是,Publa 不仅支持本地 SQLite,还支持 Turso 提供的在线 SQLite 服务。Turso 有足够小项目使用的免费额度,因此,对大多数个人博客或小团队博客来说,可以将 Publa 部署在 Vercel 上并使用 Turso 提供的数据库,完全零成本运行。

小结

以上便是对博客系统 Publa 的介绍。

你现在看到的这个博客就是基于 Publa 的,我已经使用这个系统一段时间了,将持续使用并不断改进完善它,如果你也想架设一个动态博客,不妨试一试 Publa

Publa 基于 MIT 协议开源,任何人都可以免费下载和使用它。

当然,Publa 还很年轻,且之前只有我一位用户,因此可能有一些一直没被发现的 bug,如果你发现了问题,欢迎在 GitHub 上给我提 issue

最后,希望 Publa 能帮你更好地完成博客写作!❤️

当写作软件遇上 AI:Scrivener 论坛上一场持续三年的产品哲学之争

2026-05-02 13:30:55

老牌写作软件 Scrivener 的论坛上有一篇关于是否要支持 AI 的帖子,我花了一些时间仔细地把这篇帖子以及所有回复读了一遍。读完之后,我发现这不是一个简单的“要不要 AI”的技术讨论,更是一场关于写作工具本质、创作者身份认同、以及技术边界的长期争论,也是 AI 为我们这个时代带来的冲击的一个剪影,值得写一篇博客记录一下。

背景

从 2023 年 3 月开始,Scrivener 的官方论坛上发生了一场马拉松式的辩论,主题很直白:要不要给 Scrivener 加上 AI 功能?

这条讨论一直延续到 2025 年底,公开可见的帖子超过 300 条,参与者里有职业作家、程序员、学术研究者,当然还有 Literature & Latte 公司的官方代表。

三年时间里,AI 从一个新鲜话题变成了行业标配,但 Scrivener 的立场却始终清晰而克制。

官方的态度

官方代表的回复让我印象深刻,他们没有用那种“我们永远不会”的绝对化措辞,也没有跟风喊“AI 是未来”,而是用一种近乎工程师式的冷静,把问题拆解成了几个层面。

2023 年 3 月,官方代表 kewms 说得很直接:“We are not interested in any form of subscription model.”(我们对任何形式的订阅模式都不感兴趣)这句话几乎提前封死了“把大模型月费捆绑进 Scrivener”的可能性。

2024 年 5 月,另一位官方代表 AmberV 把理由说得更清楚:他们不愿意把用户的草稿发送到第三方服务器,而消费级硬件上的本地模型“还不够有用”。这不是对新技术的忽视或恐惧,而是对隐私伦理和产品质量的双重坚持。

他们把 AI 工具类比为参考文献软件,Scrivener 可以提供接口,让你用自己选的工具,但不会替你选择、绑死、然后为它的价格变动和服务质量背书。

到 2025 年 11 月,官方给出了一句几乎可以当作政策结论的话:“是否使用 AI 工具,以及使用哪一种,最好留给用户自己决定。”

简单来说,官方温和而坚定地拒绝了在软件中集成 AI 服务。

用户的分歧

然而,讨论并不只是一场“挺 AI”对“反 AI”的二元对立。

支持者里,最有代表性的诉求不是“让 AI 替我写小说”,而是一些听起来很实际的需求:

  • 能不能帮我检查一下,这把枪在第一卷里到底有没有上膛?(长篇小说/系列小说的连贯性检查)

  • 能不能把所有角色的对话按风格分类统计一下?

  • 能不能快速识别出世界观设定里的前后矛盾?

  • 能不能帮我起草一个复杂的正则表达式?

这些需求的共同点是:它们不是要 AI “代写”,而是要 AI “帮忙分析或整理”。

反对者的担忧也不是单纯的技术恐惧,他们在意的是:

  • 隐私:未完成的草稿被送去训练,等于创意还没成型就被“偷走”了。

  • 署名:如果 AI 参与了创作,读者怎么知道哪些是你写的?出版合同里写的“100% 我的作品”还算数吗?

  • 产品定位:Scrivener 的价值就在于它是“少数仍然让人自己写”的空间,一旦加入 AI,这个定位就模糊了。

  • 成本转嫁:如果 AI 功能需要订阅,那 Scrivener 还是那个“买断制、属于你”的工具吗?

自然地,也有一些中间派,他们不反对 AI,但坚持认为更好的做法是:让操作系统或第三方工具来提供 AI 能力,Scrivener 只需要保持开放接口,而不是自己深度绑定某个模型。

一个隐含共识

读完整个讨论,我发现帖子中似乎存在一个隐含的共识:几乎没有人真正支持“让 AI 直接替作者写正文”。

即便是最积极的支持者,也只是把需求限定在头脑风暴、查询、校对、梳理、节奏分析这些辅助性工作上。反对者也基本承认,拼写检查、语法建议这类功能迟早会渗透到操作系统层面。

换句话说,真正被广泛排斥的不是“AI 辅助”,而是“把去作者化的生成机制塞进写作工具的核心”。

如果要做一个类比,大概是这样:人们可以接受用计算器做算术题,甚至能接受带计算器进入考场,但不接受用 AI 直接回答试卷。

真正的分歧

梳理下来,核心分歧其实有三条:

第一,隐私的边界在哪里?
支持者认为,只要透明告知、可以关闭,把数据发到云端就是可接受的;官方和反对者则认为,“别人(别的软件)也这么干”不代表这就是对的,尤其是当这些数据是还没发表的创作草稿时。

第二,外接工具够不够用?
官方认为,Alt+Tab 切换到另一个 AI 工具,或者用 Grammarly、ProWritingAid 这类第三方服务,已经是可行甚至更干净的方案;支持者则觉得这会打断沉浸感,制造重复的拷贝粘贴。

第三,AI 到底是“不可逆的大势”还是“被过度营销的泡沫”?
有人说“AI 会变得无处不在,不适应就会落后”;也有人说“我不认为 AI 行业能撑过五年,成本、能耗、法律风险都没算清楚”。

这三条分歧显然短时间内不会有结论,帖子中直到最后也还没有真正收敛。

一点感想

Scrivener 很克制,这种克制在这个时代显得格外珍贵。

不是因为 AI 不好,而是因为在一个“人人都在加 AI”的环境里,却有一个工具明确说“我们不替你做价值判断,你自己决定”,这本身就是一种立场。

这也符合 Scrivener 一直以来的产品哲学,它不会猜你想写什么,但会给你足够的空间和结构,让你把想写的东西组织好。它的价值不在于“替你做决定”,而在于“让你的决定更容易实现”。它不是一个“智能”工具,而是一个“强大”工具。

从这个角度看,官方的立场就很好理解了:AI 可以存在,但它应该在 Scrivener 的边界之外,由用户自己选择、自己控制、自己承担风险。Scrivener 只需要保持开放,而不是成为某个 AI 服务的前端。

未完待续

当然,故事还没结束。

Windows 侧会不会出现类似 Mac 上 Apple Intelligence 的“官方允许但不深度绑定”的路径?一些用户想要的“非生成式、但高度语义化”的功能,能否在不依赖大模型的前提下实现?版权、训练集伦理、输出的可版权性,这些法律和行业规范层面的问题,什么时候能有定论?

这些问题,到目前为止的讨论里仍然悬而未决。

但至少有一点是清楚的:Scrivener 不会因为“别人都在加 AI”就跟风,也不会因为“市场需要”就放弃自己的产品哲学。

在一个充满焦虑和跟风的时代,这种克制是一种稀缺的品质。