hel-micro 模块联邦新革命

8,043 阅读12分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解

示例名称/功能使用示范模板地址模板描述托管位置
远程ts库codesandboxhel-lodashwebpack开发与打包unpkg meta
远程 react js 组件codesandboxremote-react-compwebpack开发与打包unpkg meta
远程 react ts 组件codesandboxremote-react-comp-tswebpack开发与打包unpkg meta
远程 vue2 js 组件codesandboxremote-vue-compwebpack开发与打包unpkg metagithub.io index.htmlunpkg index.html
远程 vue3 ts 组件codesandboxremote-vue3-comps-tsvite 或 webpack开发,webpack打包unpkg meta

模块联邦之缘起

自谷歌chrome浏览器异军突起,并在2008年9月2号 正式官宣发布 v8 js引擎之后,它以极高的运行效率席卷了网络世界,同时也捕获了大量用户,这种不可阻挡的势头让其他各大科技公司(apple、moliza、microsoft)感受到了巨大的杀气, 随即大家都开始招兵买马、磨刀赫赫准备杀出一条血路,从此js引擎进入了军备竞赛时期,这其中微软甚至不惜自废IE并开始力推背后携带了微软无数心血的全新js引擎 Chakraedge浏览器,可想而知大家对js引擎这块蛋糕的重视程度有多高,而v8的诞生催化了大量的著名开源作品,让js生态一直保持着非常强劲的活力,这其中最著名的就是 2009 年诞生的nodejs,一个基于v8的服务端js运行时,让js这门语言开始从前台到后台遍地生花,以至于以下一句很早诞生的调侃话语至今还在流传:

Any application that can be written in JavaScript, willeventually be written in JavaScript.

模块化规范

nodejscommonjs模块化带到了服务器端,让大型js工程组织起来更加有条不紊,同时也带了npm这个超级杀手锏,让模块分发与共享效率提高到了前所未有的高度。

而前端应用也随着网络应用的复杂度成倍提升,导致进入代码体积进入了高速膨胀时期,这个时候急需一个有效的模块化方案来解决如何优雅拆分模块,如何提高代码复用性和可维护性等一系列问题。

此时两大主流模块化方案amdcmd开始在前端这里竞相角逐并最后各自站稳了一份很大的地盘,他们的代表实现分别是requirejsseajs,相信不少小伙伴都了解或使用过。

1.png

工程化体系

尽管requirejsseajs为前端带来了模块化规范的实现并给大型js工程注入了稳固的根基,但是仅靠模块化规范,依然不能解决如何和npm生态互通,如何管理日趋复杂的模块依赖关系,如果兼容新的js特性等一系列问题,归根结底,这里面涉及到一个关键词工程化体系,之后webpackbabel便诞生了,他们目标非常明确,解决了以下图中的4大问题

2.png

随即成为了前端开始工程化体系开发的事实上的基建标配。

webpack依靠优秀的插件和加载器机制,让其围绕它的生态得以不停地做大做强,干掉了过程其他更偏向于工具的gulpgrunt等一众对手

3.png

npm 的魔咒

webpacknpm几乎形成了完美搭档的状态,但前端原本从cdn获取的资源改由打包工具合并到一个包体里带来了致命的更新和部署效率问题。

4.png

在某些需要需要动态更新的场景,这种all in one的打包机制让包体的部署效率大打折扣,这本不是webpacknpm的问题,而是人们天生对web环境需要快速迭代、快递实验的高要求带来的典型场景需求。

注:externals 本身不能彻底解决动态更新的诉求,只适合于将底层公关依赖包体外链到cdn

bundless 来袭

同时webpack随着项目体积日趋庞大,新的问题诸如开发体验差(热更新慢)、包体加大、构建速度慢(node_modules黑洞)等问题也诞生了,此时新生代的开发工具snowvite以不打包的名义开始蚕食webpack的市场。

5.png

他们都利用了浏览器的原生模块化能力esm,跳过webpack的需要的依赖分析和打包流程,在此设计下做到了毫秒级的调试启动。

6.png

但它们带来的极致快体验并不能动摇整个webpack生态的深厚围城,事实上大家都是处于调试基于vite而生产打包还是用webpack的双擎驱动模式,毕竟esm普及还需要时间。

7.png

模块联邦吹响反攻号角

既然大家都吐槽wepack构建慢,那么可不可以有一种方式既能跳过构建步骤又能让用户可以按自己的方式组合多层次依赖模块呢?

当然有的,那就是走预构建这条道路

8.png

模块联邦因此诞生了,它的伟大之处在于保持当前前端开发模块化、组件化、工程化的高效率体系下,允许模块独立开发、独立部署,通过 CDN 直接共享,从而挣脱npm包体无法动态更新的桎梏,从而推动整个前端界开发和运行体验上升到一个新高度。

只要有越多的模块能提升到联邦里,本地启动速度将越快!

9.png

而且联邦模块天生具有双重身份,即可以是模块消费者,也可以是模块提供者,这让模块联邦应用之间形成了天然的网格关系,模块分发效率、部署效率、共享效率都得到了前所未有的提升!

a1.png

模块联邦的阿喀琉斯之踵

webpack 5或者其他工具带来的模块联邦实现真的完美了吗?它的确解决了免构建动态更新跨项目共享模块的问题

a2.png

但基于现有的编译时插件化机制去实现,无法规避工具链强绑定编译时确定才能远程模块消费关系的难题

a3.png

试想一下,你需要使用模块联邦这么技术,需要做的前置条件有多重,需要升级整个工具链!而且不同工具链之前的联邦模块是互相不通的!模块的流通性绑定在了你选择的工具链上。

模块联邦新革命

破除这两大难题的唯一解就是将其sdk化,这是hel-micro对模块联邦实现的全新思考,也是发起模块联邦新革命的秘密武器。

sdk化后,任何技术栈、任何工具链均可无损、无痛接入模块联邦技术。

a4.png

运行时的模块消费关系

从工具链回归到js语言本身,意味着模块消费关系从编译时提升到运行时,将极大提高动态载入远程模块的灵活性,为更复杂的业务赋能。

a5.png

降维打击

对比依赖工具插件实现的模块联邦,hel-micro从语言层面的实现将对其他模块联邦实现造成降维打击。

a6.png

相比传统的npm共享方式,hel-micro也具有更高效的代码共享能力(运行时共享)

a7.png

解密 sdk 化核心实现

要实现sdk化,意味着我们必须挖掘出js语言本身的隐含能力,并跳出传统的打包流程思维,才能达到我们的最终目标

异步import暗藏的能力

通常我们都会在头文件使用import关键字静态导入其他模块,但其实import可以作为函数调用,异步的导入一个模块,并返回一个promise对象

 const mod = await import('./some-mod');

所以我们可以通过微调模块的加载顺序,来达到为一个模块被其他模块静态导入之前能够为它注入新代码的效果

a8.png

而这个异步import带来的提前注入效果成为了hel-micro为模块代理对象注入远程运行时代码的关键实现点,让hel-micro可以位用户提供懒加载和预加载两种加载方式。

a9.png

上图里两个核心接口:libReady接口负责暴露模块,preFetchLib接口负责拉取模块,通过调用接口的行为让每一个模块都表现为提供方或者消费者。

运行时依赖分析

hel-micro通过内部维护的事件总线模块池样式池元数据池四个数据结构,让有多级依赖层次的远程模块得以高效并安全有序的加载。

b1.png

其中模块池能保证模块不被重复加载并被上层各方调用者重复使用。

b2.png

元数据-模块的灵魂

模块的实质是构建产物文件的集合,hel-micro通过提供构建时的插件,收集好产物的网络路径并按sdk规定的协议存储起来,得以后续可以在网络让sdk可以下载并执行所有的远程模块。

b3.png

双构建机制

hel-micro使用rollup打包本地可静态导入的代理文件,使用webpack打包远程注入的实际运行代码,来达成可以本地静态导入node_modules里的代理模块对象得到完整的类型提示,让用户能得到像使用本地模块一样地使用远程模块的极致开发体验

b4.png

内定了4个目录hel_disthel_proxyhel_proxy_eshel_bundle来承载不同的产物,供package.json配置不同的入口。

b5.png

其中hel_proxyhel_proxy_es目录下的文件是就是我们说到的模块代理对象的入口文件,我们可以看到该文件近乎一个空壳,所以它对模块使用方的打包体积大小影响几乎可以省略不计。

b6.png

平台与生态

hel-mirco sdk主要依靠规范化的元数据格式来做远程模块加载,所以只要任意用户按照规范格式提供模块的元数据,即可被hel-micro加载,这将极有利于围绕hel-micro的上层生态的建设与发展。

支持模块任意部署

sdk与平台是解耦的,我们默认提供了和hel packnpm unpkg的支持,但允许你可以将模块发布到网络上的任意文件服务,仅需知道其部署地址即可。

b7.png

如用户将 hel-meta.json 元数据保存到后台数据库(可结合devops流水线),就可很快搭出一个类似hel pack的中心化的模块管控平台

b8.png

中心化的模块管控平台对模块实施版本预览灰度放量秒级回滚等工作会特别方便,但它不妨碍sdk以去中心化的方式加载多平台包体,因为sdk天生支持同时从多个不同的平台拉取远程模块并使用,例如同时加载来自unpkg 和 hel pack2个平台的模块,平台值会被当做命名空间来隔离不同平台可能重名的模块。

b9.png

上层生态建设

hel-micro本身只提供远程模块加载的能力,具体的ui适配层还需要上层封装库区实现,目前的规划如下图

c1.png

hel-micro-react为例,提供以react钩子函数的形式懒加载远程组件,并同时提供shadow dom样式隔离功能

c2.png

微前端架构思考

这些年来大家提到微前端,几乎把该架构和技术栈无关独立部署js css运行隔离这3大特性画起了等号,但其实这些特性在针对以下诸如兼容历史遗留项目运行、跨部门合作等场景时才需要,此时我们可依托于微容器方案(原生iframe、乾坤、micro-app、无界等)做微前端架构,但如果我们收敛到单一技术栈体系下来做,其实只需要满足模块独立部署运行时组合css隔离这几个特性的话,微模块方案(模块联邦)才是正确的架构实现,它更轻量且更灵活。

image.png

当然了两者并非水火不容,它们搭配起来混合使用将是相辅相成的完美组合,你可以先使用微容器再接入微模块做跨应用模块动态共享,或先使用微模块再套上微容器做运行时隔离,取决于你的项目发展到了什么阶段(是否需要多技术栈混合开发,是否需要多版本技术栈同时迭代等)。

例如下图无界和hel-micro混合使用的案例,可以看到多个技术栈的容器里再次以模块的粒度来动态组合运行时代码的效果演示。 image.png

image.png

何时采用hel-micro

当前时刻当你遇到以下任意一种情形时,采用hel-micro都绝对值得你投入去尝试。

c3.png

npm私服可通过verdaccio搭建,版本化的cdn包管理可通过unpkg私服来做。

roadmap 2022~2023

c4.png

3闪电表示已经就绪,2闪电表示快要完成

结语

模块联邦对于搭建超大型js工程能到如虎添翼的作用,巨型应用的模块部署效率、共享效率都将迎刃而解,同时搭配微容器相关框架(如wujie、rame)等,为你的隔离运行需求保驾护航,欢迎 star hel-micro,了解并使用。


我的其他开源作品友链(欢迎关注与了解):

concent,一个自带依赖收集、提供setup特性的react数据流方案

limu,一个比immer更高效的js不可变操作库