广民广民

05 · 核心实现

本地处理的技术底线、契约执法、灰度开关——三个最值得学的机制拆解

💡 TL;DR:本章挑三个最值得学的机制拆开讲:①「文件不上传」的本地处理是怎么做到的;②契约执法除了生成类型还有什么硬手段;③灰度开关为什么能「一行常量控全站」。

一、「文件不上传」的本地处理

免费工具的全部计算都在浏览器里完成。支撑它的是三层浏览器能力:

  • File API:<input type="file"> 与拖拽把用户选的文件读进浏览器内存(ArrayBuffer,即一段字节数组),这一步没有网络参与;
  • Canvas:浏览器自带的画布,能解码图片、逐像素处理、按指定质量重新编码——图片压缩(质量 60% 重编码)和多平台裁切(按平台比例画布裁切)都靠它;
  • WebAssembly(WASM):把 C/C++ 写的程序编译成 .wasm 字节码,浏览器加载后以接近原生的速度执行。ZIP 打包解压用的 fflate(JS 实现,快)、RAR/7Z 用的 libarchive(C 库编译)、儿童入园照的人像分割模型(MediaPipe,12.2MB 引擎文件)都是这条路。

为什么坚持这么做:① 隐私是卖点——截图、证件照、内部文档不敢传服务器,但在本地处理就无所谓;② 省钱——服务器不需要带宽和算力处理文件;③ 快——没有上传下载,只有你电脑的读写速度。代价也要认:浏览器算力有上限,所以每个工具都有体积上限(单文件 20MB 一档),并且两款重工具(儿童入园照、RAR 解压)要下载本地引擎、仅电脑端可用。

服务端在免费工具里承担什么:只做两件事——把这个页面递给你、记录匿名使用计数(哪个工具被用了多少次,用于决定优化顺序)。你的文件内容从头到尾不在请求里。

二、契约执法的硬手段

04 架构设计 讲了契约流水线的四步,这里讲为什么它不会腐烂——光有生成流程不够,还得有人盯着。三个硬手段:

  1. CI 漂移红灯:流水线每次都重新 gen:openapi,和仓库里的快照做 git diff。有差异 = 有人改了接口没同步快照 = 合并被物理拦下。这不是提醒,是合并不了;
  2. 生成物禁手改:generated.d.ts(前端类型文件)文件头声明禁止手改,改了也会在下次生成时被覆盖——唯一正确的修改路径是改源头 Schema;
  3. 运行时双向校验:Zod Schema 同时用于前端表单校验和后端请求复核。前端拦一层(体验好),后端再拦一层(安全兜底),两层读的是同一份 Schema——不存在「前端放过了后端没放过」的口径差。

三、灰度开关:一行常量控全站

每个对外功能面都有一个开关常量,例如 FEATURE_TOOLS_ENABLED = true、付费工具 FEATURE_PAID_TOOLS_PUBLIC(运行时读环境变量)。它的设计要点是消费方一律读开关,不自己判断:

  • 导航项:if (FEATURE_TOOLS_ENABLED) items.push(...) ——开关关了入口就消失;
  • 站点地图:sitemap 生成读同一个开关——关掉的功能不会出现在给搜索引擎的清单里;
  • 接口守卫:后端守卫读同一个开关——直达 URL 绕过前端也进不来;
  • 工具可见性:toolVisibleToViewer() 统一判定——付费工具在灰度期对非管理员不可见,目录、详情、推荐位全走这一个函数。

收益:功能开关 = 改一行常量重新发布。上线新功能时先关着部署,验证无误再翻开;出问题一行翻回去。代价:开关一多容易「以为关了其实没关」,所以开关集中在 shared 一个文件里导出、命名强制 FEATURE_ 前缀,全局只有一处定义。

四、值得借鉴的三条原则

  1. 能用平台能力就不自建:Canvas/WASM/File API 都是浏览器白送的,自己写的只是「编排」;
  2. 同一份事实多处消费:Schema、开关、工具目录都是「定义一次、处处引用」,copies 是 bug 之源;
  3. 机器执法优先于人的自觉:漂移红灯、生成物覆盖、统一判定函数——能交给机器的纪律绝不靠人记。

延伸阅读:13 款工具各自的实现细节见 工具实现揭秘;这套机制怎么安全地上线见 06 质量门禁与部署。