
十一个工具,零网络请求:MiniTools 的离线思路
MiniTools 现在有 11 个工具。图片裁切、批处理、加水印、打码、拼图、抠图,JSON 格式化、流程图、HTML 转 PDF、表格合并、条码生成。
它最特别的地方不是功能,是一条硬规矩:打开到导出,一次网络请求都不发。
在线版 kingsir.work/MiniTools/ 也是这样。断网、飞行模式、公司内网被墙、CDN 被抽风 —— 它都照跑不误。
这篇讲这个决定是怎么来的,以及为什么它其实挺贵的。
起因:一次很小的尴尬
我原本只是想写个图片拼接的工具自己用。写完顺手放网上,分享给同事。
第二天有人跟我说:打不开了。
排查下来是 CDN 的问题。问题不大,但它让我意识到一件事 —— 一个”处理本地文件”的工具,跑起来却依赖一个别人家的服务器,这个设计本身就是拧的。
我用它处理的是我自己的图片、我的表格。这些数据从头到尾都在我机器上。结果打开页面这一步,反倒要经过别人的服务器才能开始。
这说不通。所以我决定不这么玩了。
做法:把依赖搬进自己家里
MiniTools 里用到的第三方库有 Mermaid、SheetJS、JSZip、heic2any、html2pdf、JsBarcode、Vue 3。
正常做法是走 CDN,一行 <script src="..."> 完事。改成全部本地化,就是把每个库的文件塞进对应工具的 vendor/ 目录,然后引本地路径。
改完的代价是这样的:
| 工具 | 本地依赖 | 体积 |
|---|---|---|
| 流程图编辑器 | Mermaid | 约 3.2 MB |
| 图片批处理 | heic2any + JSZip | 约 1.4 MB |
| 表格合并 | SheetJS + Vue 3 | 约 1.0 MB |
| HTML 转 PDF | html2pdf.bundle | 约 885 KB |
| 条码生成器 | JsBarcode + JSZip | 约 155 KB |
| 图片裁切、图片加水印 | JSZip | 各约 95 KB |
| 抠图、打码、拼图、JSON 格式化 | 无 | 0 |
流程图编辑器最惨,为了一个 Mermaid 背上 3.2 MB。移动网络下打开大概要一秒多的等待。
有人问过:值吗?加载慢换取断网可用,到底哪个更重要?
我的判断是要看工具的性质。图片拼接这种事,我随时随地做,地铁里、飞机上、山里,都可能。慢一秒我能忍,打不开我忍不了。
而且这个”慢”只发生在第一次。之后浏览器缓存就吃上了。
一个自己踩的坑:不是所有工具都需要 JSZip
一开始我图省事,十一个工具里凡是涉及多文件导出的,全部塞上 JSZip。
后来发现不对。
JSZip 是干什么的?把多个文件打包成 ZIP。那如果一个工具只导出一个文件呢?比如图片裁切,勾了三张图,导出三个独立 PNG —— 那就是三个下载,根本不需要”打包”这个动作。
于是我调整了策略:只有真正需要”打成一个文件”才引 JSZip,其余走原生。
原生打包长什么样:
// 没有 JSZip 的地方,直接用平台 API
function downloadOne(blob, filename) {
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = filename;
a.click();
URL.revokeObjectURL(a.href); // 记得回收,不然后面会卡
}
需要打包多个的时候,浏览器有原生接口:
// 批量导出:一次触发多个下载
urls.forEach((u, i) => setTimeout(() => triggerDownload(u, names[i]), i * 120));
那个 i * 120 是有讲究的 —— 浏览器会拦截”连续多次自动下载”,间隔开就能逐个触发。不加这个延迟,用户点了导出只下来一个文件,然后以为坏了。
抠图、打码、拼图、JSON 格式化这四个现在依赖是 0,加载打开即用。
数据不出浏览器,是顺便获得的
做”零网络请求”的时候,我没想到会附带一个副产品:数据天然不可能泄露。
因为没有任何上传接口,图片、表格、JSON 全都在页面的内存里处理完就走。你传给它的东西,物理上就没有离开过你的电脑。
这年头说”数据本地处理”的话挺多的,验证成本却很高 —— 你得读代码,或者抓包。
而”整个页面不发出一个请求”这件事,是可以当场验证的:打开开发者工具的网络面板,一路操作到底,面板干干净净。一句话说清,不用解释。
界面:跟所有工具保持一致
技术上有意思的部分到此为止,说点务实的。
十一个工具如果各有各的按钮风格、深浅色方案、语言切换逻辑,那就不是一个”工具集”,是十一个割裂的小网页。用的人得重新适应十一次。
所以统一了三件事:
深浅双主题。 默认跟系统,右上角能切,选择记在 localStorage。导航页和十一个工具页行为完全一致 —— 这点很重要,要是首页能切、内页不能,那就裂开了。
中英双语。 每个页面右上角 中 / EN,选择记在 lang,默认跟随浏览器语言。十二个页面共用同一套对照表。
视觉语言。 对齐近黑前景 #171717、纯白底、细灰边框、黑底胶囊主按钮。用 GitHub / Vercel 那套审美,因为它已经被验证过足够耐看,不用我再调。
配色上反馈有统一规则:成功和信息用前景反色(黑底白字),只有错误用红色。这样页面里出现红色的地方,一定是出事了,不用自己去找。
顺带说一句抠图
图片抠图那 个工具我原本以为会是纯靠库。看了源码才发现是自己写的算法,核心就三行:
band = clamp(round(min(W, H) * 0.04), 2, 128); // 只采样四条边的窄带
d = sqrt(dr² + dg² + db²) / √3; // 归一化到 0-255 的颜色距离
d <= t → alpha = 0 // 认定为背景
d >= t + soft → alpha = 255 // 认定为前景
其余 → alpha = 255 * (d - t) / soft // 中间地带羽化过渡
逻辑是:抠图的本质是”判断哪些像素是背景”,而背景色几乎总是出现在画面四边。所以只采样边缘一圈窄带(宽高的 4%,2~128px 之间),做直方图取频次最高的几个颜色当候选背景。
这里有个细节我挺欣赏的:勾选”保留主体内部同色区”时,它会从四边做一次泛洪填充,只有跟边缘连通的才算背景。所以白衬衫上的白、白背景里的白,能分开。
手动擦除和恢复记在独立的 delta 层里,不跟计算结果混在一起。所以你调完容差重新算,之前手工擦的地方还在。
这种”算法简单但把边界情况想清楚”的感觉,是我觉得 AI 写代码最出彩的时候 —— 不是难,是细。
最后
MIT 许可,随意改。仓库在 isnotry/MiniTools,每个工具目录下都有一份 README 说明功能与依赖。
如果你发现哪个工具缺了什么功能,或者觉得某个交互别扭,直接提 issue 或者改了推上来都行。