在设计一套客户端软件,尤其是基础模块时,我们通常会考虑到扩展性。而插件机制是一套非常好的解决思路,可以让其他开发者按照我们预期的路径进行功能扩展。
那么在软件初期,我们要如何设计一套较为完善的插件模式呢?本篇将会从我个人对插件的懵懂认知开始,逐步介绍如何形成一个较为完善的解决思路。
怎么理解插件?
Plugin(Plug-in, addin, add-in, addon 或 add-on)是一种计算机应用程序,它和主应用程序(host application)互相交互,以提供特定的功能。
首先,我们经常会在各种软件里面看到 "Plugins"
等关键字,那怎么理解这个词呢?
在我刚接触计算机的时候,看到一些大型软件里面会说可以装某个插件,从而实现一些功能,当时对插件的概念就是,这是一个对主应用程序的扩展,大多都是一些小功能。比如Photoshop
里面的抠图插件,后来又出现一些滤镜插件,调色插件等等,其中大部分还是需要 付费 才能开通的插件。
在大学开发和销售微信墙的时,在 “Money💰” 的驱使下才算对插件有了真正的概念 😂
可以看到,整个产品首先是基于互动墙源码(包含 站点的后台设置、前台展示、移动端展示、基础数据库表等),这部分基础功能包含了整体的软件形态、插件设计和数据流向。
可以说基础包里面的内容是技术难度最大,代码最多的部分,但是其定价却是最低的。我的客户可以以最低的成本获得到我的核心模块,但是它却没有什么实质性的互动功能;而互动的能力都是以各种插件作为增值服务进行选购,从而构建了一个完整的 “有利可图” 的 B2B2C 互动生态。
在用户接入部分,通过插件,客户可以根据自己的用户群体选用平台进行接入:
- 微信公众号
- 企业微信
- 钉钉
- 微博
- …
在互动玩法部分,客户也可以通过选用自己活动所需要的互动插件;每个插件都是独立的源码包,选用安装后方可在后台中找到选项开启。
通过这套基础的插件化设计,我构建出了一个完整互动模块系统,它可以为我构成一个小小的商业模式:
- 渐进式的服务体验,客户门槛低
- 可升级、可复购、可定制的持续商业关系
- 独立、安全的插件市场
我大学的“小摊子”也正因为这样的“耗散结构”运转下,越做越大。后面包含所有插件的完整源码可以卖到 8000 一套。
所以怎么理解插件?在我的概念里,插件最终应该是一个独立的安装包,在安装这个插件之前应用程序中应该不包含任何与这个插件有关的代码、配置、数据表。这样我才可以拿去安全地卖更多的钱钱 🤩,不是吗?
而在技术上来看,插件其实是 AOP 设计的一种产物,它把内核部分作为一个开放式模块,将主流程的各个部分“开槽”开放给插件定制,从而达到功能可插拔,渐进式扩展的工程化方案。
所以,在插件设计时,我们可以先确认这个基本指导原则,非侵入式,非具象需求,只把控主应用流程。
怎么设计底座?
底座,也就是内核,先有内核模块才能有插件生态。首先,我们在做一个基础底座之前,一定是要搞清楚我们这个底座的定位是什么。
拿一些典型的例子来说明:
- IDE,是编码
- 播放器,是播放
- 浏览器,是看网页
- Webpack,是编译
搞清楚核心定位后,首先要做的是 完成最小单元的开发建设,并且在这个最小单元的流程节点上“开槽”。
比如,我们正在做一个针对 运营、产品、技术的工具库:i18n Tools
其主要定位是提供一套实用工具库来帮助公司内部各环节的同事,快速了解和改善 Lazada 卖家中心的体验。通过这套工具,我们可以提供给所有安装了工具的同事们一条便捷的路径来了解和改善我们的产品。
从这个产品定位来看,它的主路径就是打通浏览器扩展,提供一套能够将 页面元素和产品数据关联 同时还具备插件开关、自动更新等能力的 基础交互模型。
基于这个定位,底座要考虑的事情就会有以下几项能力:
- 版本检查,自动更新
- 插件功能管理(开关、配置)
- 页面元素索引、定位及渲染方案
- 浏览器扩展方案
所以在这种基础底座能力开发过程中,就不要有关于具体功能的逻辑掺杂。
比如:需要找什么样的元素、找到元素后应该如何渲染,这些具体的业务逻辑便可以直接交由插件来具体实现。
这个过程看似浑然天成、理所应当,但在项目初期大多是以一个具体的功能需求作为目的,所以实现过程中很容易写出脚本式的代码,忽略了可以作为内核能力提供的交互模型抽象。
还有一点,在底座开发完成后,一定需要自己做一两个预置插件,完全套用底座所提供的开槽实现,如果自己开发插件的过程都觉得很别扭那一定不是一个好的底座设计。
关于如何构建插件,我之前在文章《为你的 JavaScript 库提供插件能力》有过一个解决方案,感兴趣可以阅读。
怎么设计插件?
首先在设计设计插件前期之前还是务必要了解主基座的 生命周期 和 预制插件,好比我们写一个 Webpack 插件或者 Vite 插件。如果在还没有了解其本身内核的运行机制、生命周期、接口定义就着急动笔的话,很可能出现各种实现都非常奇怪,时常侵入修改内核的上下文以便达成插件效果。
实际上,尤其是在开发这种大型开源项目插件时,要相信如此多的开发者曾经一定遇到过相似的场景,开发插件时如果遇到一些无法解决的问题时,可以按照以下步骤寻求解法:
- 查看接口定义,看看该接口中还有什么可以用的方法
- 想想官方提供的插件中,是否有类似场景(一般来说一定会有)
- 去对应社区搜索相关问题,看看能否找到一些相似的仓库借鉴
- 如果还是不能解决,或许要重新想想这个功能的实现,是否适合放在这个插槽里了
当然,这里面也有例外,对于一些较为初期的项目,最好还是在开发插件之前能够认真阅读下内核源码和预置的插件源码。对于初期项目,还没有经历过很多用户“磨炼”,实际上插件开发者就是要抱着Contributor
的心态,如果遇到一些实在需要扩展解决的问题,也要抓住机会,大胆提出 PR。
另外插件上线完之后,作为插件开发者也需要多多订阅和关注核心库的更新日志(CHANGELOG),对于一些严谨的内核项目一般都会在发布 X、Y 位版本之前预先发出beta
或RC
版本。
这个时候插件开发者还是要抽时间跟进维护,否则很容易因为主库的版本更新导致插件不可用。如果遇到Break Change
更新,在更新插件版本时,记得对旧版本做主库的上限版本控制,如: ~2.0.0
,保持旧版本的可用性。
总结
随着 Web3.0 不断发展,互联网客户端插件化开放会越来越明显,插件设计、模块设计 都是构建自己生态圈,实现社区共赢,达成商业激增的有效手段。
今后这种插件化接入其他生态、或者构建生态的方式只会越来越多,作为前端开发者最好能够提前适应这种模式,总结出一套这种场景的应对方法。