基于 vue 如何实现一个可插拔式的系统

2019-07-10 09:40:40 +08:00
 duanzs

简单来说:写一个大项目,然后有很多小项目,大项目可以动态引用小项目(页面或组件)而无须重新打包发布 举个例子:大项目就好比是 vscode,小项目就是 vscode 的一个个插件,可以随意下载使用插件而不用每次都更新 vscode 版本,并且插件可以单独升级

4809 次点击
所在节点    问与答
43 条回复
doublleft
2019-07-10 11:50:55 +08:00
@duanzs 我也遇到并设计过这样的系统,并且走了很深(将近一年),分享下历程 希望对你有帮助吧。

可插拔系统要求的是各个小组件组成的大系统。小组件设计要支持单独新增、发布、独立测试,大系统不用一起打包发布。我的业务场景是实现一个几百张的表单、表格、图形的查询页面。很多组件是可以重用的。

1. 最开始是用 iframe 实现,封装成大系统+若干小系统,每个系统都是独立的 react 工程,关键点是要解决的是组件之间通讯问题。后来因为维护太多小系统,又用 webpack 封装了统一的打包发布脚手架和 CI/CD,这是第一版。

2. 第二版我动手设计了一个 runtime 的渲染项目,把之前的小工程改为 npm 包 一个小组件。根据访问 router 向服务器拉取组件配置,这个配置描述了页面有哪些组件、他们的位置 类别 事件 关联组件等,比如 input 放在哪里。这些颗粒组件也是事先写好、独立测试发布、低耦合、不含业务的。不过最终还是有拆解不了的,当时解决办法就是单独放在业务工程,打包好一起下发。

这样做看似优雅,其实还是有很多问题和优化空间,比如线上并行版本太多、很考验封装能力、框架升级要 rebuild 所有,要严格的设计状态控制等。

其实如果继续发展下次我想应该是一个类似“中台”表单设计器,我当时已经封装了不少基础组件( Input、Button、Select、Radio 等),定制了超级复杂的 Table (支持多表关联 下钻 子 Table 固定行列),又整合了 E-Chats 进去,又实现了全数据驱动,完全可以实现运营同学手动拖拽设计表单。

这套系统根据业务需求,最后支持全国几万家门店定制的数百个表单查询,不过后来因为不是重点项目,慢慢就被剥离出来了。
wly19960911
2019-07-10 12:06:27 +08:00
@Rorysky 前端的 script 标签不是动态链接库了?

他要的不是动态链接库,而是动态链接模块,在原有的运行模块中再次启动一个模块。

我只学过 Java,我不知道怎么在运行中的项目怎么插入一个 controller …至少可能实现也很麻烦。
Rorysky
2019-07-10 12:09:23 +08:00
@wly19960911 不扯别的,本质都是 对象 及 接口调用
loading
2019-07-10 12:13:00 +08:00
@SilentDepth 还敢用 cdn 不指明版本号?上次圣诞节下雪的事忘了?
wly19960911
2019-07-10 12:13:14 +08:00
@Rorysky 但是框架没有整合,现在要求框架拥有动态模块的能力。就跟楼上一样对框架进行了 hack 或者直接 script 使用框架的库(只有库),做到当然可以。
SilentDepth
2019-07-10 12:46:38 +08:00
@loading #24 我有建议不指明版本号吗?都说了 element-ui 和 vue-router 是针对「异步加载」的举例,咱能聊聊异步加载方面的事儿不?

而且,antd 跟版本号有啥关系?你怎么知道你锁定的不是下雪版本呢?
lecion
2019-07-10 13:11:09 +08:00
听起来有点微前端的意思
duanzs
2019-07-10 14:06:03 +08:00
@SilentDepth 所以这就是区别点,因为我需求是预先不知道有哪些子模块
duanzs
2019-07-10 14:06:21 +08:00
@Rorysky 能详细一点吗
duanzs
2019-07-10 14:07:13 +08:00
@learnshare 对,所以我就是问怎么解决
duanzs
2019-07-10 14:09:01 +08:00
@Rorysky 是的,所以现在前端很多东西都在借鉴后端
duanzs
2019-07-10 14:12:30 +08:00
@doublleft 感谢分享
SilentDepth
2019-07-10 14:30:17 +08:00
我不明白你为什么要纠结于「是否预先知晓模块信息」这一点。

所有模块都可以转换成一个 JS 文件。我们约定这个 JS 文件会以某种方式输出一个 Plain JS Object,并且包含一个 install 方法,这样就可以通过 Vue 插件机制 ( https://cn.vuejs.org/v2/guide/plugins.html) 动态注册到 Vue 应用中。接下来你只需要解决「获取这个 JS 文件」的问题即可。解决起来也不难,理论上任何异步加载 JS 资源的方案都可以。在整个这个过程中你的主业务应用不需要做任何变化(当然主业务应用本身要实现插件系统的底层支持)。

至于从哪里加载,当然会有一个后端服务提供模块清单(就好比 VS Code 的插件搜索),但这个事情是业务的事情,并且与主业务应用无关了(除非搜索功能本身是主业务应用提供的)。确定待加载模块列表(不论是自动生成的还是用户手动选择的),转换为各模块的 URL,依次加载对应的 JS 文件并调用其中的 install 方法(通过 Vue.use( )),一个「插」系统就实现了。

这里唯一需要额外实现的是「拔」,因为并没有一个 Vue.unuse( ),Vue 也不识别 uninstall 方法。但既然模块本身已经在本地了,手动调用 uninstall 方法并没有什么障碍,实现相应的卸载逻辑即可。
duanzs
2019-07-10 16:20:54 +08:00
@SilentDepth 关于纠结 纠结于「是否预先知晓模块信息」这一点:我觉得是我连子模块信息都不知道更谈不上引用了。
关于整体回复:我觉得主项目使用 Vue.use( )之后不应该还得去把主项目打包发布吗?
SilentDepth
2019-07-10 16:30:12 +08:00
@duanzs #34 你是不是误解了什么。

function loadExternalModule (url) { document.createElement('script') /* ... */ }

有了这样一个函数,只要能有一个 url,是不是就可以在运行时加载一个外部 JS 模块了呢?至于模块信息……你(的业务)总得设计一个机制来生成或获得模块信息啊,不然插件系统做出来要怎么用?

function useExternalPlugin (plugin) { Vue.use(plugin) /* ... */ }

有了这样一个函数,只要能有一个插件对象(通过 loadExternalModule( ) 获得),是不是就可以在运行时注册一个外部 Vue 插件呢?完全不需要重新编译主项目。
sodatea
2019-07-10 16:36:15 +08:00
不知道你需要的是不是这种模式:
https://medium.com/@cramforce/designing-very-large-javascript-applications-6e013a3291a3

enhance instead of import
duanzs
2019-07-10 16:46:42 +08:00
@SilentDepth 感谢老哥指点,对这块确实基础薄弱
这里我再 引申 /直白 一下问题。
举一个例子,有一个子项目,比如说是用 vue 写的一个单页计算器
子项目肯定是以单独项目的方式去打包,再以某种方式发布到主项目中。子项目( webpack )打包出来可能会有 index.html、bundle.js 、css、大图片等。这种怎么去 use。
duanzs
2019-07-10 16:47:45 +08:00
@sodatea 打不开,尴尬
sodatea
2019-07-10 16:56:24 +08:00
@duanzs medium 被墙了,自行翻出去吧……
SilentDepth
2019-07-10 17:09:01 +08:00
@duanzs #37 这个要分情况了。

如果子项目本身就可以独立运作并发挥业务价值(有自己的业务环境、root 实例、入口页、整体布局等),只是在特定情况下需要「嵌入」到一个更大的应用中,但内外并没有太多交互,那么你可以考虑 <iframe>——这种情况,你只是希望两个应用显示在一个页面中。(这种情况讲道理不能算「插件」。)

而,如果你不想用 <iframe>,你可以考虑 Vue-in-Vue (我瞎编的术语)。子项目的也是一个完整的 Vue 应用,其实只是需要一个 mountpoint 而已,那么主项目应用留出这么一个元素让子项目去 mount 即可。此时子项目的 install 方法就是一句 new Vue(...).$mount(...)。

如果子项目是一个业务模块,需要依赖外部环境才能发挥业务价值,那么子项目的 index.html 应该只是为了方便独立开发调试而存在,真正 build 出来的应该是一个库(也就是 dev 和 build 是两套打包流程)。剩下的就跟 element-ui、vue-router 等没什么本质不同了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/581581

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX