返回博客
tech 2026-03-28

我们为什么选 WebAssembly 而非云 API

我们为什么选 WebAssembly 而非云 API

一个深夜,我们盯着一份云账单与延迟直方图。每次流量飙升都意味着又一次关于自动扩容、冷启动、以及"是否真的需要为合并两页 PDF 把它运过半个地球"的讨论。那一晚我们问了一个简单问题:如果让浏览器承担重活会怎样?

改变架构的那一刻

Ai2Done 起源于一个信念——生产力工具不该要求你把合同、发票、家庭照片交给第三方服务器。云 API 强大,但带来永久依赖:你的字节离开你的机器、穿过你不掌控的网络、落在你永远无法审计的硬盘上。对许多工作流这是可接受的;对我们的用户是底线问题。

WebAssembly 给我们一条可信路径:让真正的代码——从 Go 编译,也就是我们服务端用的语言——跑在用户标签页里。与其画一个把文件上传到黑箱的瘦客户端,不如交付一个本地执行的可移植二进制,性能可预期、零意外出口流量费。

为什么不"就用" JavaScript?

我们爱 JavaScript 做 UI 与编排,但把 MB 级的 PDF 逻辑或密码学搬进手写 JS 既上线慢、又难与 Go 工具链保持同步。WASM 让我们复用久经考验的库(pdfcpu 风格管线),并在栈两端共享类型与测试。浏览器变成宿主,不再是重写目标

JS 里一段最小桥接加载 .wasm 模块,传文件句柄与进度回调,再渲染结果。其余留在 Go:

// 概念示意:WASM 导出薄 API;真正工作留在 internal/tools。
func ProcessPDF(input []byte) ([]byte, error) {
    // 合并、拆分、加密——全部确定性、全部本地
    return merged, nil
}

我们获得了什么

结构性隐私。处理在 WASM 中进行时,我们不必写"我们可能处理你的文件"的隐私政策——我们可以说"我们做不到",因为字节根本不离开设备。这不是营销,是物理。

真正重要的延迟。批量操作里,网络往返常常主导耗时。本地 WASM 把这变量完全消掉。进度条反映真实 CPU 工作量,而非某地区队列深度。

成本可预期。我们的基础设施账单随站点流量伸缩,而非随用户文档的字节数。这种对齐让我们专注于产品质量,而不是为每次转换计费。

我们坦诚接受的取舍

WASM 不免费。初始下载体积重要,所以我们用 Brotli 压缩、按工具懒加载、设合理文件上限——给出友好提示而不是标签页崩溃。浏览器内存上限是真实的;我们记录它们并暴露进度,让用户知道应用在工作而不是死了。

我们仍然在服务端做该做的事:认证、搜索索引、静态资源交付。服务端管身份与发现,客户端管变换——这种分工是刻意的。

展望

选 WASM 而非云 API 不是赌云会失败——而是赌用户主权。每次发布新工具时,我们都先问:能不能在本地跑?这条纪律让 Ai2Done 保持快、诚实,并与每天依赖它的人对齐。如果你也在这个领域里建造产品,希望我们的路径给你许可去质疑"上传再处理"的默认管线——并让浏览器做超过它一向获得的功劳。