夜雨聆风学习资料网

ARTICLE · 1175955

插件简史:一部「开放」与「控制」的摆荡史

插件简史:一部「开放」与「控制」的摆荡史

插件简史:一部「开放」与「控制」的摆荡史

从 Emacs 用 Lisp 写自己,到 VS Code 把扩展关进独立进程——四十年里,插件系统反复回答同一个问题:把多少权力,交给外人?

核心命题:插件系统的四十年,不是一条平滑上升的进化曲线,而是一台在"开放"与"控制"之间来回摆动的钟摆。每一次把门打开,生态就繁荣,风险也跟着进来;于是平台方回手把门关紧一点——关太死,生态又喘不过气,于是再松开。本文沿着这条主线,把从 1976 年 Emacs 到 2015 年 VS Code 的关键转折点串成一条线。


引子:你每天用的东西,背后是一套很古老的把戏

你打开 VS Code 写代码,装了十几个扩展;你用 Chrome 上网,浏览器右上角蹲着七八个插件;你用微信,顺手点开一个小程序——这些体验看似毫不相干,底层却是同一套"把戏":

宿主提供一个稳定的契约,外人按契约往里塞功能,宿主在需要时把它接上、用完再收回。

今天我们把这叫"插件系统"。但很少有人知道,这套把戏在 Web 还没影儿的年代就已经玩得炉火纯青。而且从第一天起,它就卡在一个永恒的张力里:你想让外人帮你把生态做大,又怕外人把你的房子拆了。

这篇文章不堆术语,只讲一个故事:四十年里,几家最有代表性的系统,是怎么一次次把"开放"和"控制"这杆秤拨来拨去的。

开放↔控制:插件系统的钟摆


一、三种原初形态:Web 之前,答案就齐了

要证明"开放↔控制"的摆荡很早就在,得回到 Web 之前。有三家系统,分别贡献了三种根本不同的扩展哲学,而且每一家都栽在不同的"控制/风险"坑里。

1.1 Emacs:当"宿主"本身就是扩展语言

Emacs 的故事最早可以追到 1976 年——MIT AI 实验室的 David Moon 和 Guy Steele 把一组 TECO 编辑器宏称作 "Editing MACroS",扩展的雏形就是"把一个按键重定义成一段程序"。

真正的分水岭是 1985 年 3 月 20 日:Richard Stallman 发布 GNU Emacs v13,把 Emacs Lisp(Elisp) 作为首要扩展语言。编辑器核心用 C 写,但几乎所有"功能"都是 Elisp——宿主和扩展用的是同一种语言、跑在同一个进程里。

这带来了两个后果:

  • •

    强大:编辑器能"自举、自省",连帮助系统都是 Elisp 写的。你想改它的任何行为,写段 Lisp 挂上 hook(钩子)即可。

  • •

    原罪:零隔离、靠信任。一个劣质包就能让整个编辑器卡死。更狠的是,1986 年 Markus Hess 就利用 GNU Emacs 邮件子系统的漏洞,拿到 Unix 机器的超级用户权限——这是"扩展能力过宽→被利用"的最早真实案例,比浏览器扩展恶意化早了二十年。

Emacs 证明了插件系统的第一种极致形态:宿主与扩展同语言、同运行时。这条"同进程深度扩展"的路线,日后在 OSGi、VS Code 早期实验里反复回响——而它的代价"同进程风险",正是后面"隔离模型"要回答的原罪。

1.2 HyperCard:当"文档"本身就是可扩展的应用

1987 年 8 月 11 日,Apple 发布 HyperCard,定价 49.95 美元,买新 Mac 还免费送。它被称为"software erector set(软件积木)"。

它的野心很不一样:让非程序员也能造交互应用。Bill Atkinson 的团队设计了 HyperTalk 脚本语言,并允许用 XCMD / XFCN(用 C / Pascal / 68000 汇编写的原生代码模块,打包进 Mac 资源叉)直接嵌进一个 stack(文档)里。

这意味着什么?——扩展跟着文档走,文档本身就是应用。你发给我一个 stack,我打开就能用里面的功能,不用单独"安装插件"。

它栽的坑也极其深刻:

  • •

    平台锁定:只能在 Mac 上跑,几乎不可移植。

  • •

    零隔离:第三方原生代码可直触系统资源,无任何护栏。

  • •

    平台方收回控制权:Apple 始终没想清楚 HyperCard 的"目标市场",Jobs 回归后把它砍了,2004 年停售。一个激发无数人"第一次编程"的平台,因为它的拥有者收回控制权而消亡。

这是"控制摆荡"最残酷的一课:平台方手里的开关,比任何架构缺陷都致命。

1.3 Photoshop:当"文件夹"本身就是插件市场

1990 年 2 月 19 日,Photoshop 1.0 发布(仅 Macintosh)。从第一天起它就带着 filter(滤镜)/ 插件架构——Knoll 兄弟把一些"古怪"的图像效果做成可独立增删的插件文件,丢在 Plug-ins 子文件夹里。

这件事的意义被严重低估:

  1. 1.

    "plugin"这个词在消费软件里普及,就是来自它把滤镜做成"plug-in 文件";

  2. 2.

    文件夹即发现机制——日后 npm 的 node_modules、VS Code 的扩展目录、WordPress 的 wp-content/plugins,骨子里都是同一个"丢进去就被发现"的直觉;

  3. 3.

    第三方市场是"意外"放出来的:Knoll 兄弟只想隔离自己的烂滤镜,结果养出了 Kai's Power Tools(1992)这样的明星生态;

  4. 4.

    平台方用 SDK 许可收放控制权:2002 年 Adobe 收紧 SDK 访问,2005 年又改为 Web 表单免费申请,当代更迁移到 UXP(用 manifest.json + Web 技术 + 受控 C++ 入口)——从"文件夹放任"回到"声明式 + Web 沙箱"的控制。

1.4 小结:三家的共同原罪

三者共同前提:扩展与宿主同进程、零隔离,靠信任或契约而非沙箱。这正是后续"隔离模型"要解决的"前状态"——而隔离的演进(独立进程 / iframe / Wasm / 容器),是贯穿整部史的一条暗线。


二、摆动的钟摆:Winamp 与"社区文化"的失控

1997 年 4 月 21 日,Nullsoft 的 Justin Frankel 发布了 WinAMP 0.20a(名字是 Windows + AMP 解码引擎的合体),作为免费软件。6 月 7 日,版本改名为 "Winamp"(v1.0 / 1.006),补齐了频谱分析仪,经典的"小灰窗 + 绿字"界面成形。

Winamp 的杀手锏是 皮肤引擎 + 插件架构:到 1998 年底,社区已经做出 60 多个插件、上百套皮肤。输入插件(MP3 / MOD)、输出插件、可视化插件、DSP 插件——你把功能拆成一个个独立的小模块,任何人都能写。

这是"开放"最甜蜜的时刻:一个二十出头的开发者,靠社区就能把一款媒体播放器变成文化符号("Winamp, it really whips the llama's ass" 成为一代人的梗)。

但钟摆开始回摆。1998 年 1 月 Nullsoft 正式注册公司,1999 年 6 月 AOL 以 8000 万美元收购了 Nullsoft。当一家媒体巨头握住了开关,社区的"失控式繁荣"就被纳入公司战略——Winamp 3(2002)那次"重写一切、抛弃旧插件和皮肤"的翻车,正是"控制方强势改规则、老生态断裂"的早期样本。

这一幕,和二十多年后平台收紧 SDK、下架第三方插件的剧情,如出一辙。


三、纯插件架构的巅峰与代价:Eclipse 与"plug-in hell"

进入 2000 年代,插件系统迎来一次范式跃迁:不再只是"给应用加功能",而是"应用本身就是用插件拼出来的"。

IBM 的 Dorian Birsan 在 2005 年 3 月的 ACM Queue 上写道:传统的插件是"外接的附加件",而新的**纯插件架构(pure plug-in architecture)里,"一切皆插件",宿主退化成一个运行时内核(kernel),负责"找到、加载、运行插件 + 维护注册表 + 解析依赖"。插件之间通过扩展点(extension point)与扩展(extension)**互相挂钩——插件自己也能成为别的插件的宿主。

这套思想的工程落地是 Eclipse。Eclipse 3.0(2004 年)以 OSGi / Equinox 作为运行时内核,用 plugin.xml / manifest.mf 声明元数据,靠类加载器做模块隔离。从几十个插件,到后来企业级产品轻松突破上千个插件,Eclipse 甚至把可扩展性目标抬高到"支持 5,000–10,000 个插件同时跑"。

但 Birsan 在同一篇文章里泼了冷水,提出了一个著名概念——"plug-in hell"(插件地狱):

  • •

    安装与更新噩梦:插件来自不同作者,组合从未被整体测试过;

  • •

    安全黑洞:任意插件拥有对宿主的"无限访问","除非你小心挑选装了什么,否则几乎无法确信装进去的不是恶意的";

  • •

    并发版本地狱:同一个插件装了两份、版本冲突、菜单出现重复项……

这是"开放"走到极致后的必然反噬:当一切皆插件,依赖图、版本、安全就同时爆炸。OSGi 用"类加载器隔离"部分解决了隔离问题,但"插件地狱"至今仍是大型插件系统的梦魇——它直接预告了后面"声明式清单 + 版本化 + 隔离执行"三条现代防线。


四、回摆"控制":浏览器战争的两次收敛

2000 年代末到 2010 年代,插件系统的钟摆明显摆向"控制"——而且两次收敛都发生在浏览器上,驱动力都是安全 + 性能。

4.1 Firefox:从 XUL 的"无限自由"到 WebExtensions 的"有限沙箱"

Firefox 早期允许扩展用 XUL(XML 用户界面语言)+ XPCOM 深度改写整个浏览器界面,自由到离谱。但自由的反面是脆弱:这种扩展能直触浏览器内部、性能拉跨、安全难控。

  • •

    2015 年 8 月,Mozilla 宣布弃用 XUL / XPCOM 旧扩展体系,转向 WebExtensions API;

  • •

    2017 年 11 月 14 日,Firefox 57(Quantum)发布,WebExtensions 成为唯一受支持的扩展 API,旧版 XUL 扩展一律失效。

这一刀切,让无数老扩展(包括一些用户离不开的经典工具)一夜之间作废。Mozilla 的理由很硬:多进程架构(Electrolysis)+ 更严格的沙箱,需要把扩展从"能碰内部"收进"只能走受控 API"。

4.2 Chrome:Manifest V2 → V3,把"能力"收进白名单

Google 走得更远。Chrome 的扩展模型从 Manifest V2 演进到 Manifest V3(MV3):

  • •

    2018 年宣布 MV3;

  • •

    2021 年 1 月(Chrome 88)MV3 普遍可用;

  • •

    2024 年 6 月(Chrome 127)MV2 被彻底弃用。

MV3 的几记重拳:后台页从持久进程改为 Service Worker(省资源、可被回收);declarativeNetRequest 取代旧的 webRequest 拦截(广告拦截器不能再逐请求读写流量);禁止远程代码执行(逻辑必须随包静态审核)。

这些改动技术上合理(性能、隐私、安全都更优),但 declarativeNetRequest 对内容拦截能力的限制,直接引爆了广告拦截器社区的强烈反弹——这恰恰是"控制摆荡"的最佳注脚:平台方出于安全/隐私收权,却动了插件的饭碗,于是"治理"与"生态利益"正面相撞。


五、现代综合:VS Code 把钟摆停在中间

2015 年,Microsoft 推出 VS Code,它的扩展系统像是把前四十年的教训都消化了一遍:

  • •

    进程隔离:扩展跑在独立的 Extension Host 进程里,扩展崩溃不影响主进程——直接回应 Emacs / HyperCard 的"同进程原罪"和 Birsan 的安全担忧;

  • •

    声明式契约:package.json 里的 activationEvents(何时激活)+ contributes(贡献什么能力)+ main(入口),让宿主不执行代码也能理解插件——这是"清单驱动"的现代范本;

  • •

    API 显式版本化:engines.vscode 约束兼容边界,避免"宿主一升级插件就断";

  • •

    能力收敛:扩展通过约定 API 面影响系统,不能直接碰 DOM。

VS Code 没有消灭"开放",而是把开放装进契约 + 生命周期 + 治理的笼子——这正是本系列贯穿的灵魂框架(下一节展开)。它证明:摆荡的终点不一定是某一个极端,而是一套让"开放"可持续的护栏。


六、四十年摆荡史:一条时间线与三个命题

把上面的故事连起来,就是一条清晰的钟摆曲线:

1976/1985  Emacs      → 开放(自由软件+自举),但零隔离、治理集中1987      HyperCard   → 开放给终端用户,却平台锁定、最终被平台方放弃1990      Photoshop   → 文件夹放任 → SDK收口 → UXP Web沙箱(控制回收)1997–99    Winamp      → 社区失控式繁荣 → 被 AOL 收购收编2004      Eclipse/OSGi→ 纯插件架构 + 模块化,但"plug-in hell"2015→17    Firefox     → 弃 XUL,Firefox 57 仅支持 WebExtensions(收敛控制+安全)2018→24    Chrome MV3  → 为安全/性能/隐私强制 Service Worker、收紧权限2015–      VS Code     → 独立扩展宿主进程 + 声明式清单 + 显式版本化(综合)

插件系统四十年摆荡时间线

这条线回答本文开头的核心问题——插件系统如何演化?——可以收成三个命题:

  1. 1.

    开放带来繁荣,也带来风险;控制收拾风险,也伤生态。 每一次摆荡,都是"让外人进来"与"把外人管住"的再平衡。

  2. 2.

    隔离是贯穿全史的暗线。 从 Emacs 的同进程信任,到 VS Code 的独立进程,再到 Wasm / 容器——信任级别每降一档,就要多一层隔离来兜底。

  3. 3.

    清单、版本化、生命周期,是让"开放"可持续的三道护栏。 它们把"不确定性"关进契约与治理的笼子,而不是靠"信任"或"运气"。


结语:下一次,我们先问"插件到底是什么"

这部史前史留下一个更根本的问题:我们说了半天"插件",可它和模块、扩展、包、组件、微服务,到底差在哪? 为什么有的"插件"是文件夹里的一个 .dll,有的却是微信里的一个小程序?

下一篇(01《插件是什么》),我们不从历史出发,而从本质出发,给"插件"划出一条干净的边界——并借用两个物理世界的老祖宗(榫卯与乐高)来说明:好的可组合性,从来都是"强契约"与"标准接口"的合谋。


#插件系统 #插件架构演化 #开放与控制的摆荡史 #plugin

相关学习资料