开门
你用过很多插件。给 VS Code 装过语言支持,给 Chrome 装过广告拦截,在 Mihon 里加过一堆漫画源,也许还给 Obsidian 写过一个小脚本。它们都能凭空往一个已经写好的程序里塞进新功能。
但你有没有想过:这怎么可能?VS Code 的作者不认识那个插件作者,代码没一起编译过,甚至不是同一年写的 —— 凭什么它装上就能用?插件的代码是什么时候、被谁、怎么变成进程里活着的对象的?它想读你的密钥文件时,谁在拦?它把自己写死循环了,为什么 VS Code 不跟着卡死,Obsidian 却会?
这本书的答案是:插件系统是一笔关于信任的交易。你把程序的一部分权力,交给一段你没写过、没审过、随时会变的代码。剩下的所有设计 —— 扩展点、清单、类加载器、沙箱、权限、版本号 —— 都是在给这笔交易定价:让它能做多少事,和它能害你多深。
我们先拆开五个真实系统,看它们各自把价钱定在哪;然后从零造一台自己的。那台宿主是真的 —— 用 JS 写出来,就在这些网页里跑:你现写一段插件代码,点「装载」,它真的被加载、真的注册命令、真的改变宿主的界面;你去掉一个权限,它真的当场被拒绝。
这本书的主线
插件系统是一笔关于信任的交易:你把程序的一部分权力,交给一段你没写过、没审过、随时会变的代码。
每一个你听说过的机制 —— 扩展点、manifest、类加载器、独立世界、沙箱、权限清单、semver 范围 —— 都是在给这笔交易定价:让它能做多少事(能力),和它能害你多深(控制)。
五个真实系统之所以长得完全不一样,不是因为技术水平有高下,而是因为它们把价钱定在了不同的地方。看懂了这一点,你再看任何一个插件系统,都能在三分钟内说出它的取舍。
五个系统,一根轴
把它们按「插件能拿到多少能力 ←→ 宿主保留多少控制」排开,是这样的:
| 系统 | 插件是什么 | 怎么进来 | 隔离 | 换来了什么 |
|---|---|---|---|---|
| Obsidian | 一个 main.js |
在 Electron 渲染进程里直接 require |
没有。官方明说无法可靠限制插件权限 | 插件能做的事没有上限 —— 长出了别处长不出来的社区 |
| Mihon | 一整个独立安装的 APK | 用自己的类加载器把它的 DEX 拉进本进程,反射实例化 | 同进程同权限,靠签名信任把关 | 一千个漫画站的适配,由一千个人在宿主之外各自维护 |
| IntelliJ | 一个 jar + plugin.xml |
同一个 JVM,但每个插件一个类加载器 | 类空间隔离(各带各的库版本),但没有 CPU / 内存隔离 | 插件能深度改造 IDE 的每一层,代价是它卡死你也卡死 |
| VS Code | 一个 npm 包 + contributes |
另一个进程(extension host)里 require | 进程边界。拿不到 DOM,所有调用过 IPC | 插件永远卡不死编辑器;UI 风格统一;代价是几乎全异步 |
| Chrome 扩展 | manifest.json + 几段脚本 |
按清单分派:service worker、注入页面的独立世界 | 最强:独立世界 + 进程沙箱 + CSP + 权限逐项授予 | 恶意扩展也得先向用户要到权限;代价是能力被切得很碎 |
没有哪一头是「对」的。Obsidian 那头换来的是不设上限的创造力,Chrome 那头换来的是可控的风险。你造插件系统时的第一个决定,就是在这根轴上选一个点 —— 后面所有技术选择都由它推导出来。这本书会带你把这根轴走一遍,然后帮你选。
「亲手造一台」是什么意思
市面上讲插件系统的文章,大多停在「画个架构图 + 讲讲概念」。这本书不是。我们要造的宿主是一个真的、能跑的程序,它就嵌在这些网页里,用 JavaScript 写成,从第 6 章起一路长大:
- 第 6 章,它第一次能加载插件 —— 你在文本框里现写一段插件代码,点「装载」,宿主用
new Function把这段字符串变成活对象、调它的activate(),它注册的命令真的出现在命令面板里,点下去真的执行; - 第 8 章,它学会读清单和拒绝 —— 你把宿主版本从
1.0.0改成2.0.0,原本能装的插件当场全被拒; - 第 14 章,你会真的看着一个插件逃出沙箱,用三行代码从一个「什么都没给它」的
ctx里把整个globalThis拿回去; - 第 15 章,一个插件的死循环会真的把这个页面卡住 0.7 秒 —— 你自己就是那个用户;换进 Worker 之后,同样的重活,心跳一格不漏;
- 第 17 章,你勾掉一个权限,插件的调用当场抛出
PermissionDenied; - 第 23 章,所有零件合起来 —— 一个清单里只声明了
notes.read却偷偷调fs.read的「效率助手 Pro」被装上、启用、拦截,而其它三个插件毫发无损。
正文里出现的每一个数字,都是这台宿主当场算的,不是我写死的:那段笔记是 36 个字、来回启停 5 轮之后残留 5 个监听器、宿主升到 2.0.0 时生态存活率从 75% 掉到 20%…… 全部来自真的运行。
写给谁
这本书写给这样的人
- 一直好奇「插件到底是怎么被装进去的」,但没找到过讲清楚的东西
- 想给自己的程序开个口子,不知道从哪开、开多大
- 用过 Mihon / VS Code / Obsidian,甚至写过插件,但对宿主那一侧完全是黑箱
- 能读 JS 或 Kotlin 其中一门(书里两种都有,互相对照)
- 想要的是「亲手造一遍」那种理解,而不是背几个名词
这本书不做的事
- 不是某一个系统的插件开发教程 —— 那些官方文档写得比我好
- 不教你怎么绕过某个具体软件的限制
- 不深挖 JVM 字节码、V8 内部实现、操作系统沙箱的底层细节
- 不假装存在「最佳插件架构」—— 全书都在讲取舍,不在讲答案
六卷
关于书里的事实
五个真实系统的机制细节,都来自各家官方文档与源码(Mihon 的加载流程直接读了 ExtensionLoader.kt),并已核对到 2026 年 7 月的现状。凡是带时效的说法(比如 Chrome 商店 2026 年 8 月 31 日下架最后一批 MV2 扩展、Mihon 当前支持的扩展库版本),书里都会标出来源和时间点 —— 因为这类东西一定会过期,你该知道它是什么时候的话。
书里出现的比例类估计(比如「生态里大约多少插件写 ^1.x」)会明确标成假设,不会伪装成实测数据。
卷 I 到卷 II 建议按顺序读,它们在建立整本书的词汇表(扩展点、契约、清单)。之后如果你只关心某一块,可以直接跳:想搞懂加载去卷 III,想搞懂安全去卷 IV,正在被版本兼容折磨去卷 V。
每一章末尾都有一节「装到自己身上」,把这一章的机制翻译成你能立刻用的东西 —— 大多给的是 Kotlin / Android 的落地写法(ServiceLoader、DexClassLoader、版本契约),因为那是最常见的「我想给自己的 App 加插件」场景。