探索

只有画面这一层会化 — 无需安装的应用,以及编译冻住的东西

作者: makemind · 2026年7月10日

创刊号刊出了《无需安装的应用的时代》。它画的是:一处十分钟的修改,要走到用户画面上会经历什么 — 构建、审核、分发,以及等着用户去按更新 — 那整条锁链。

下一期刊出了《我们为什么放弃了编译》。它把那条锁链的第一环当作一种物性重读了一遍。编译,就是把那一刻的所有决定冻成一整块硬邦邦的东西这句话,是这本杂志六个月里写出的最好的概念压缩。

两篇是同一道山脊的正反面。一篇问锁链消失后会变什么,另一篇问它当初为什么会在。所以谱系、三种绕道尝试、反驳问答,都各立了两遍。这次合成一篇。

而合并时还有一处要改。两篇都在没有数字的情况下谈数字。

「画面定义很小。不是沉重的代码包,而是『一个按钮、一段文字』这样轻的规格。」

写了「小」,却没量过是多少字节。写了「一边三天,一边十秒」,却没给那十秒计时。当时没有可量的东西。现在有了。

冰 — 编译做的事

先说要留的。把编译读作物性的那一段原样保留。

做一个应用,就是把那一刻的所有决定成一整块。按钮在哪、什么颜色、按下去会发生什么 — 这些决定经过编译在设备里凝固。凝固之后一个字符都改不了。要改就得再冻一次、再过一次审核、再分发一次,然后等用户愿意收下。

分发之痛不是因为懒惰或工具老旧。那是「画面」这个决定被冻在设备里这一结构的必然。

这里立起一个问题 — 原文所定式化的那个问题。能力和冰真的分不开吗? 要用原生能力就必须冻起来,还是可以只化掉画面这一层、把能力那一层继续冻着?

我们花六个月真的做了一遍。先写答案。能分开。而且必须继续冻着的东西比想象中多。

定义到底有多小 — 实测

先偿还原文最弱的那一句。「定义很小」没有数字。

现在可以真的量了。以下全部是从跑起来的东西上取到的大小。

什么大小
STM32H723 开发板递来的 ui://app(一个页面)656 B
ESP32 递来的 ui://app(带路由的应用)158 B
该应用的第一个画面 ui://page/main1,894 B
门店服务器的自助机画面1,241 B
同一服务器的 POS 画面1,170 B
同一服务器的后厨画面1,144 B
无人店铺整个包(4 个 JSON)6 KB

一个画面是 1〜2 KB。「小」这个说法是对的。只是当时并不知道它对不对就写了,现在知道了。

有了数字,原文说不出来的一件事就挂上了。门店服务器分别递出三个画面,三者合计 3,555 字节。若按旧办法,这里本该做平板应用、POS 应用、后厨应用三个,各自过审、各自分发。不是三个应用被 3.5 KB 取代了,而是三个应用存在的理由没有了。

(测量出处:接到一块芯片上,接上的设备就成了终端 · 开发板递出自己的画面 · 一个文件夹就是应用

十分钟的修改要多久 — 实测

原文所画锁链的另一端。不冻起来的话,真的立刻抵达吗?

我们改了无人店铺应用的画面 JSON 再跑一次。日志如下。

[+ 1415ms] edited ui/pages/main.json — 2940 B -> 2934 B, no compiler ran
[+ 1419ms] route "/" -> ui://pages/main  ("Unmanned Store")
[+ 1498ms] captured 04_edited_json.png

从编辑到画面反映是 83 毫秒,编译器没有运行。 路由跳转是 4 毫秒。因为画面切换就是查一次文件。

原本写着「十秒」的地方,现在填进了真实数字。而这里也暴露出原文没看见的一件事 —83 毫秒不是因为没有构建,而是因为没有分发。 就算构建缩到 0.1 秒,只要审核、分发和用户点更新还在,锁链就原封不动。化掉的不是编译,而是整条锁链

什么至今仍必须冻着

这是两篇里《放弃了编译》最好的一节,而且真做出来之后证明那一节是对的。只是原文也把它写成了叙述。

实时循环保持冻结。 在 LCD 面板样例里,温控循环在 C 固件内部。

/* First-order thermal: dT/dt = k·(target − T). Converges to target
 * with a little sensor noise. */
static void thermal_update(void) {
    /* ... */
    double alpha = 1.0 - exp(-k * dt);
    g_temp += (g_target - g_temp) * alpha;
}

这东西下不到画面定义里来。以毫秒为节拍转动的东西,不能待在网络的另一头。

安全范围也保持冻结。 而且这一条更硬 — 不管上面让它做什么,硬件都不会越过自己。

/* The relay clamps its own range. Whatever the rule upstream
 * decided, the hardware still refuses to exceed itself. */
if (v < 0) v = 0;
if (v > 100) v = 100;

原文写的是「实时、驱动、安全保持冻结,只有画面层变成液体」。做出来之后发现那份清单还得加一行。自己守住自己极限这件事。 规则是液体,就意味着规则可能是错的;那时最后拉住的那只手,必须在冻着的那一侧。

支付授权路径也保持冻结。 刷卡终端的安全区域不打开。不是打不开,而是不打开更好,理由另写在不拿出来的东西守住了应用

(固件边界见LCD 厂商 30 分钟做出 HMI,继电器钳位见换掉传感器,控制照旧

协议的名字 — 两篇一路都没喊出来的

两篇都把这个协议一路留作无名。「约定好的语言」「开放的共通语言」。一篇主张「不开放就不成立」的文章,如果把协议所指模糊掉,读者就无处可去核实。

它的名字是 MCP(Model Context Protocol)。而且这个系列的每一个样例都说它。真实的字节长这样。

=> {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18", ...}}
<= {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-03-26",
     "capabilities":{"tools":{},"resources":{}},
     "serverInfo":{"name":"WeAct H723 MCP Node","version":"0.1.0"}}}

这是真实的 STM32 开发板通过 USB 串口回的一行。而「它是开放的」这个主张现在也有了依据 —同一份客户端代码接上了 STM32(UART)和 ESP32(Wi-Fi TCP)。 两块板互不知晓,是不同公司的芯片,传输也不同。它们仍然通了,因为说的是同一个协议。

原文写的那句「和浏览器能画出任何一家公司服务器的 HTML 是完全同一个原理」,如今不再是比喻,而是两块板的日志。

再答一次那些反驳 — 这次带数字

两篇各立了五组问答,其中四组重合。只立一次,能答得上的就改过来答。

「每个画面都从服务器取,不会慢吗?」— 画面定义是 1〜2 KB(见上表)。而往返取决于链路。在某一次运行里,UART 侧的工具往返是 1〜11 ms,Wi-Fi TCP 侧是 21〜69 ms,从连接到第一个画面是 46 ms 对 189 ms。所以准确的答案是这样 —不是「不慢」,而是链路不同、量级不同,而这个差别会分开 UI 的设计。 个位数那一侧可以指望「一按就有」;几十毫秒那一侧就必须把等待状态画到画面上。原文不知道有这个分叉。

这里补一句。时间值不可复现。 大小是文件,量多少次都一样;而延迟每次运行都在晃,某一次运行里 Wi-Fi 那侧超过了上述数值的两倍。所以这篇文章只主张量级,不主张精确的毫秒。

「离线呢?」— 这条还还不上。原文写着「定义会被缓存」,而这个系列的任何样例都没有试过缓存与离线行为。 重连行为也没量过。作为未验证保留。

「安全呢?」— 原文的答案 — 运行时不信任定义、只允许固定的组合 — 依然成立。在此加上做出来之后学到的。边界不是一道而是三道(接收侧的运行时、暴露侧的设计、守住自己极限的硬件),而不在工具清单里的能力,人去按也好模型去选也好都够不着。实证在另一篇里。但「所以它是安全的」这个系列没有证明 — 认证、权限、传输安全全在外面。

「应用商店政策呢?」— 原文的答案原样保留(界线在于是否植入任意原生代码)。原文自己加的但书也原样保留 —这是政策解读,不是法律意见。 这里再加一条:两篇都没有引用哪怕一行依据条款。没有引用的解读不是解读而是猜测,所以这一项在补上引用之前标记为未解决。

「服务器成本呢?」— 保留原答案。只是这个系列做的服务器全是本地进程,我们从来不在能测量运营成本的位置上。

这个样例没有做的事

原来两篇的诚实一节保留。编译不会消失,只是换了地方;既有系统不会一夜变身;叫做设计的那份劳动依然存在。做出来之后,三条都成立。

在此加上这次学到的。

两篇的思想实验就是思想实验。 门店设备的场景也好、创刊号的场景也好,都是以「假设」开头的想象,却因为写得具体而读起来像观测。这篇文章引用的全部是运行日志和实测,并逐条用链接标明来自哪一篇。我们不把想象和观测排成同一种字体。

测量很窄。 上面的数字来自一台 Mac、一台路由器、一个房间,多数只量了一次。样本少、环境单一。大小是确定的(文件就那么大),而延迟每次运行都会变 — 就在写这篇的过程中,先前引用的延迟数值因为重跑而变了,我们为了与刊出的日志对上又改了一次。引用时间的文章,会和它的日志一起变老。

三件事仍未偿还。 离线与缓存行为、应用商店政策的依据引用,以及运营成本。原文给过答案的地方,至今没有实测。

所以,再怀疑一次

原文就是这样收尾的。去怀疑,接下来的文章会拿实物给你看。

六个月过去了,那些文章出来了。这篇引用的数字全部来自它们,每一篇都附着构建日志、运行日志和无头拍下的画面。想复现,把文件夹拿走跑一遍就行。

而我们仍然留着可以怀疑的地方。离线没量过,政策没挂依据,成本我们不在能测的位置上。 那三件事还停在当初的位置。

画面这一层化了。这件事现在可以用 656 字节和 83 毫秒来说。什么必须冻着也知道了,而那份清单比一开始想的要长。剩下的是还没量的,那些等量过之后再写。


makemind.dev 「探索」— 当时没测就写下的数字已换成实测。

Twitter