在 DSH 里开发插件最爽的方式是「动态插件」:即写即用、即时热更,特别适合迭代。但它有个致命短板——重启即失。我做的下载管理器一开始就是动态插件,每次重启都要重新加载,烦得不行。于是走上了「固化」之路,途中还让整个前端崩过一次。

什么是动态插件

动态插件是运行在沙箱里的一段代码,通过专用接口定义:定义即记录、运行即生效,还能热更新。开发体验一流,但一切副作用都随进程消失。想要「开机自启、重启自动加载」,就必须固化成正式包。

固化的标准姿势

固化其实不复杂,就是把动态代码变成一个本地包 + 一条启动配置:

node_modules/@local/my-plugin/     ← 固化包
├── package.json                   ← 声明入口与客户端入口
├── lib/
│   ├── index.js                   ← 主逻辑(服务端)
│   └── client.js                  ← 界面逻辑(浏览器端)
└── 配置注入                        ← 在启动配置里挂载该插件

挂载后重启服务,插件随主程序自动加载,从此「固化」完成,再也不用手动恢复。

那次崩溃:沙箱全局的陷阱

第一次固化时,我直接把动态插件的代码原样搬进包——然后整个界面崩溃,报错 ReferenceError。排查后明白了:

动态代码里依赖的沙箱全局,固化环境里不存在

动态插件能用的某个专用方法,是沙箱专属的全局;固化后代码跑在正式环境,这个全局根本没被定义,一调用就 ReferenceError,直接带崩整个界面。

✅ 解法:固化版必须改用框架官方提供的注册接口——用 HTTP 端点注册替代沙箱方法,浏览器端用 fetch 访问端点替代专用调用通道。

这条经验让我之后写所有插件都先问一句:这段代码依赖的是「官方接口」还是「沙箱福利」?依赖沙箱福利的,固化必炸。

固化后的收益

版本迭代的哲学

我的下载管理器从动态版一路迭代到固化的 v18:每次升级都只动一处、验证一处。固化的价值不只是「重启不失」,而是让插件从一个试验品变成一件作品——能装、能卸、能分享。

动态是开发模式,固化是交付模式。开发时贪图热更的爽,交付时就要扛起稳定的责——两条路都要会走,才知道什么时候该切换。

想看固化后的成品?去插件页看 dsh-download-progress,它的完整进化史(含踩坑)都写在仓库里。