打开网易新闻 查看精彩图片

一、一个文件,装下一整套程序

在浏览器里双击一个 2.43MB 的 HTML 文件,你得到的是一个完整的 wiki:能新建、能编辑、能搜索、能打标签、能装插件、能换主题。不用安装,不用数据库,不用服务器,也不用联网。

更反常识的是它的内部结构。把这个文件拆开逐段量一遍:整个文件 99.7% 是脚本,真正属于"网页骨架"的部分只剩 0.2%。而整套程序的核心——353 个 JavaScript 模块、1,066 个语言包,合计 2,276 条子组件——被压进同一个条目,这一条单独就占了文件的 81.0%。

它叫 TiddlyWiki。第一个版本发布于 2004 年 9 月 30 日,到今天 8,041 天,22 年。2007 年,它的版权被交给一家非营利组织托管。官方文档至今保留着那句卖点:数据放在哪里由你决定,几十年后你依然能用今天记的笔记。

二、程序和数据,为什么能装在同一个文件里 一切皆条目

TiddlyWiki 只有一种基本单位,叫 tiddler,可以理解成"一条笔记"。特别之处在于,在这套系统里连程序代码也是 tiddler:那些 JavaScript 模块被打上 application/javascript 类型标记,和你的笔记用同一套容器装着。

数据与代码因此没有分家。既然两者都是条目、都活在同一个内存结构里,把它们写进同一个 HTML 文件就顺理成章了。官方开发者文档把这套做法叫"微内核":启动时先运行一个极小的内核,建出 $tw 对象,剩下的模块再作为普通 tiddler 被加载进来。

保存这件事也被拆成了三个角色:deserializer 负责把 tiddler 读进来,saver 负责把整个仓库一次性写回,syncadaptor 则按条同步。单文件模式用的是 saver,配上 Node.js 运行时就用 syncadaptor——同一套程序,既能当一个文件用,也能当一台小服务器用。

它到底被用来做什么

Erlang 的共同发明人 Joe Armstrong 在官网的推荐语里写过一句:这是他找到过的、用来整理自己想法的最好的软件。

这不是玩具。因为它的渲染管线本身就是用 wikitext 写的,界面可以被捏成待办清单、演示文稿、个人数据库、菜谱集,也可以直接生成静态网站。文件里那 1,066 条语言包,覆盖三十多种语言。

它本身就是一个 Quine

Quine 指能输出自身源代码的程序。TiddlyWiki 是一个实用型的 Quine——点击保存时,它做的不是"把改动追加进数据库",而是把整个自己重新生成一遍,数据和代码一起重写。这就是它能自给自足的根本原因:它本来就具备生成自己的能力。

作者 2015 年在英国政府数字服务署做分享时,幻灯片上给的原话就是这一句:It's also a Quine.

体积究竟分布在哪

下面这段脚本可以在本机把它的结构量出来。

import re, json, oss = open("empty.html", encoding="utf-8").read()size = os.path.getsize("empty.html")print("文件大小: %.2f MB (%d 字节)" % (size / 1048576, size))blocks = re.findall(r"]*>(.*?)", s, re.S)print("script 块: %d 个,占文件 %.1f%%" % (len(blocks), 100 * sum(map(len, blocks)) / size))raw = re.search(r'class="tiddlywiki-tiddler-store"[^>]*>(.*?)', s, re.S).group(1)store = json.loads(raw)print("store 区里的 tiddler: %d 条" % len(store))core = [t for t in store if t["title"] == "$:/core"][0]print("其中 $:/core 单条占文件: %.1f%%" % (100 * len(core["text"]) / size))inner = json.loads(core["text"])["tiddlers"]print("$:/core 内部打包的模块 tiddler: %d 条" % len(inner))

本机跑出来的结果:

文件大小: 2.43 MB (2552335 字节)script 块: 4 个,占文件 99.7%store 区里的 tiddler: 6 条其中 $:/core 单条占文件: 81.0%$:/core 内部打包的模块 tiddler: 2276 条
22 年时间线

时间

事件

2004-09-30

首个版本发布,作者 Jeremy Ruston

2007

BT 收购其公司 Osmosoft,Ruston 出任开源创新主管;同年版权交由 UnaMesa 托管

2011-11

Ruston 宣布离开 Osmosoft,并承诺继续开发 TiddlyWiki

2013-12

TiddlyWiki5 发布,基于 HTML5 完全重写;原版改名为 TiddlyWiki Classic

2020-04-17

被 Product Hunt 收录,拿到当日第 2 名

2025-08-07

发布 5.3.8

2026-04-20

发布 5.4.0

2026-07-10

发布 5.4.1,至今仍在更新

GitHub 上的项目仓库在 github.com/TiddlyWiki/TiddlyWiki5,目前 8,674 star、1,252 fork、12,960 次提交。

2007 年那件事,准确说法是版权托管

许可证正文里两段版权声明并存,这两行就是全部答案:2004 到 2007 年归 Jeremy Ruston,2007 到 2025 年归 UnaMesa Association。授权方式始终是 BSD-3-Clause,一套至今有效的宽松开源许可。转出去的是版权的托管权,不是把作品丢进公共领域,也不是放弃权利。官网对作者身份的表述,也已经改成由一群开发者共同打造、属于一个庞大的用户社区。

三、长寿的账,只是换了个地方收

先看它最聪明的一步。TiddlyWiki 没有把"格式能不能活下去"这件事扛在自己肩上——它不像数据库那样需要承诺若干年后仍读得懂自己的文件,它的存档格式就是 HTML,也就是全世界浏览器都必须向后兼容的那个格式。它等于把兼容性的账单转给了浏览器厂商。

作者在分享里讲得更直接:传统的客户端服务器架构,把用户变成了只能租用服务的人,他称之为现代版的中世纪租佃关系。把数据放进一个自己能拿走的文件,是他给出的答案。

但账单不会消失。格式从没坏过,一直坏的是"写回文件"这条路。

网页脚本出于安全被禁止直接写本地磁盘,所以"保存"这个最基础的动作,22 年里至少换了五代方案:

代次

保存机制

结局

1

IE 的 ActiveX 文件对象

随 IE 退场

2

TiddlySaver Java 小程序

浏览器淘汰 applet

3

Firefox 的通用 XPConnect 文件接口

被 Firefox 移除

4

TiddlyFox 扩展

Firefox 57 起失效

5

HTML5 下载回退,用 data 地址触发另存为

目前仍可用

官方的合规写法只有五行:

var link = document.createElement("a");link.setAttribute("target", "_blank");link.setAttribute("href", "data:text/html," + encodeURIComponent(text));link.setAttribute("download", filename);document.body.appendChild(link);link.click();document.body.removeChild(link);

每一代都不是它自己淘汰的,是被浏览器更新淘汰的。一个把长寿当核心卖点的软件,它最稳定的部分和最常重写的部分,恰好是同一件事的两面。

这套设计的代价同样清楚。所有数据活在一个文件里,就意味着它天生不擅长多人协作和大规模检索——文件越大,浏览器要一次性加载的东西就越多。社区的解法是把它拆成文件夹模式,或者干脆跑成 Node 服务,但那已经是另一种产品形态了。单文件带来的干净,是靠放弃一些东西换来的。

还有两处流行说法需要修正。

第一,"一个人做了 22 年"要加限定。维基百科资料栏的开发者一栏写的是 Jeremy Ruston and community members,官网写的是由一群开发者共同打造。GitHub 的数字也支持这个口径:Ruston 本人提交 3,849 次,是第二名 562 次的 6.85 倍,但仍只占全部 12,960 次提交的约 29.7%。准确说法是——他发起、并且长期主导,但今天这是一群人的工程。他如今经营着一家自己的公司,官网首页挂着聘请 TiddlyWiki 创始人的入口。

第二,"送给了非营利组织"不等于"放弃版权"。BSD-3-Clause 仍在生效,托管的是版权的持有方式,不是把作品交出去任人处置,更不是放弃权利。

四、它给普通人的一条实用结论

对普通人,这件事的落点很实在:一份资料能活多久,取决于它存在哪。在线服务一停服,数据跟着走;一个本地 HTML 文件,只要你还能找到浏览器,它就在。

对开发者,它是零依赖交付的极限样本——把格式押在开放标准上,通常比押在自己身上更长寿。把三个长寿项目摆在一起看,路线差异会更清楚:

项目

起点

到今天

长寿靠什么

SQLite

2000 年

26 年

靠不变,接口与文件格式几乎不动,官方承诺格式支持到 2050 年

Word for Windows

1989 年

37 年

靠变,doc 换成 docx、WordBasic 换成 VBA,每十年换一次发动机

TiddlyWiki

2004 年

22 年

外壳不变、内核重写,并把格式兼容性外包给 HTML

不过要补一句:零依赖不等于零维护。保存机制和浏览器版本绑在一起,就意味着使用者要接受定期导出、必要时迁移的成本。这也是它给所有人的提醒——别把"文件还在"和"文件还能用"当成一回事。

真正值得检查的只有两件事:这份数据能不能导出成通用格式,导出之后还有没有别的软件打得开。两条都满足,才算把自己的东西拿在手里。

五、一个文件,还是一堆服务

一个 2.43MB 的 HTML 文件,装着 353 个模块、1,066 个语言包,跑了 22 年,版权托管给一家非营利组织。它的长寿不靠锁住用户,靠的是把"能打开"这件事交给最基础的标准。

你现在记笔记的地方,是租来的,还是自己的?如果明天那个服务关掉,你手上的东西还打开得了吗?