手把手教你定制一套适合团队的微前端体系

手把手教你定制一套适合团队的微前端体系
最新回答
星雪伊

2020-11-22 07:21:33

编者按:大家在使用目前市面上的微前端解决方案时,可能会有些顾虑。比如遇到框架自身的问题和坑点,影响了业务进度怎么办?现在有这么一款框架Satum,可以像express/koa框架一样提供中间件机制,其只负责核心的功能(规则计算和微应用加载/卸载)。可以基于该框架定制一套适合团队自身业务的微前端体系,另外该框架还有更多特性,如面向多实例集成、适配多终端等。

写在前面

随着前端工程复杂度逐渐增加,业务复杂的前端项目需要拆分成多个项目来维护。还有就是业务存在了3-5年,一些技术栈没及时升级,已经无法在其基础上做开发,需要搞个新项目一起集成。又或者是想通过新业务,对新技术/框架最新大版本试用。遇到上述场景(当然不限这些场景,还有更多场景待挖掘),大家也许会把目光转向微前端方案。

市面上也有一些微前端框架的实现,但黑盒化太严重。遇到问题/坑点,只能等框架的作者修复了才能正常工作。但业务开发时真正遇到了问题,如果寄希望于框架作者/社区,会不会感到凉凉?

本篇我们基于Satum定制一套微前端体系出来。Satum是一款面向多实例集成、功能可插拔的微前端框架,旨在解决多实例模式的微前端场景(当然单实例也包含其中)。即多个微应用被同时激活时,该如何协调加/卸载、数据依赖、组件共享及渲染顺序的问题。且支持中间件&插件机制,可以很方便自定义沙箱、路由协调、缓存等。通过不同的中间件&插件组合,可以定制适合自己团队的微前端架构体系。

Satum的优势(相较于社区常用的方案qiankun和micro-app):

?:完全支持,?:不支持,??:弱支持

Satumqiankunmicro-app使用简单,无需适配代码即可集成无需顷祥适配强依赖需要扩展性,控制流程中的输入或输出????定制化,支持定制适合业务的微前端体系???多应用同时激活,微应用调度和路由协调支持极弱规则限制挂载点基于路由规则,同个应用可渲染到页面不同处???多终端支持,相同URL在不同端激活不同的微应用???共享机制,支持共享三方包和组件强弱弱区块机制,可以加载任意UI组装页面???支持Vite,沙箱环境中无缝支持???微前端体系

微前端体系包含微前端框架、配置中心、微应用开发配套工具及配套服务、物料市场、低代码平台等。体系示例图如下:

带来的好处

微前端体系带来的好处不言而喻,简单说来,使用该体系可以将一个大型项目拆分成多个子项目,每个子项目各自独立,互不影响,又互相关联。核心价值是什么呢?

技术栈无关主框架不限制接入应用的技术栈,微应用具备完全自主权

独立开发、独立部署微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新

增量升级在面对各种复杂场景时,通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略

独立运行时每个微应用之间状态隔离,运行时状态不共享

所需的工程板块

从上述体系架构图可以很清楚地看到,我们需要以下工程板块:

微前端框架

建议利用@satumjs/core的定制化能力,打造适合团队的微前端框架。不太建议使用市面上黑盒化的微前端框架。这样做,可以利用其中间件/插件机制,定制个性化的微应用处理逻辑。

配置中心

所谓配置中心,其实是一个可视化的json编辑。如果有个线上的配置中心,可以灵活控制业务板块的热更新。有些业务需求,不需要修改和发布代码就能达到目的。

微应用开发配套工具及配套服务

配套工具和服务太重要了,因为我们开发的微应用不是完整的业务,有些功能模块甚至是从其他微应用共享而来。虽然微应用可以独立运行和部署,但开发过程中,还是需要及时看到集成后的效果。

物料市场

这块主要是基础组件和业务组件,可以是基于ant-design或其他design搞的组件家族。

低代码平台

微前端能够集成低代码平台生成的页面,我觉得也很必要。这样的好处是扬长避短,低代码平台一般不太建议搭建业务复杂的页面,所以单独存在的低代码平台意义有限;而纯代码打造的平台工期较长,需要的前端能力也较高;如果能够做到简单页面低代码实现,复杂逻辑纯代码实现,然后集成在一起,是最理想的方案。

使用后达到的目标

微应用合理分拆,独立运行和部署,降低整体复杂度;

技术库无关,可以根据业务需激乎蠢要尝试各种组件库和技术栈;

增量升级,新老明陪系统可以集成在一起,新系统负责新业务功能;

多终端适配,Satum支持同一个URL不同终端唤起不同的微应用;

单一职责,让诸如React/Vue等只做视图方面的事情,业务逻辑在其他微应用上承载;

可自行定制沙箱、代码处理、路由协调等功能;

如何定制初始化项目新建文件夹,添加必要的依赖

新建文件夹mf2e-test,通过命令echo{}>package.json或yarninit初始化package.json。我们使用rollup做构建工具,需要先安装构建相关的依赖。

yarn?add?rollup?@rollup/plugin-babel?@rollup/plugin-commonjs?@rollup/plugin-node-resolve?rollup-plugin-dts?rollup-plugin-terser?@babel/cli?@babel/core?@babel/preset-env?@babel/preset-typescript?typescript?rimraf?--dev

之后再创建构建相关的配置文件.babelrc、rollup.config.js、tsconfig.json

{"presets":?[?"@babel/typescript",?"@babel/preset-env"]}

import?commonjs?from?'@rollup/plugin-commonjs';import?{?nodeResolve?}?from?'@rollup/plugin-node-resolve';import?{?babel?}?from?'@rollup/plugin-babel';import?{?terser?}?from?'rollup-plugin-terser';import?dts?from?"rollup-plugin-dts";import?*?as?pkg?from?'./package.json';

constextensions=['.js','.ts'];constplugins=[commonjs(),nodeResolve({extensions}),babel({extensions,include:['./src/*/']}),terser(),];

constumdConfig={input:'src/index.ts',//此处name是会在window上附加,需注意请自定义output:{dir:'lib',name:'mf2eTest',format:'umd'},plugins,};

constesConfig={input:'src/index.ts',external:Object.keys(pkg.dependencies),output:{file:'lib/index.es.js',format:'es'},plugins,};

constdtsConfig={input:'./src/index.ts',output:{file:'./lib/index.d.ts',format:'es'},plugins:[dts()]}

exportdefault[umdConfig,esConfig,dtsConfig];

```json{??"compilerOptions":?{????"module":?"es2015",????"target":?"es5",????"lib":?["esnext",?"dom"],????"baseUrl":?"./src",????"esModuleInterop":?true,????"noImplicitAny":?true,????"strictNullChecks":?true,????"noImplicitReturns":?true,????"noFallthroughCasesInSwitch":?true,????"noUnusedLocals":?false,????"noUnusedParameters":?true,????"suppressImplicitAnyIndexErrors":?true,????"allowSyntheticDefaultImports":?true,????"emitDecoratorMetadata":?true,????"experimentalDecorators":?true,????"removeComments":?true,????"sourceMap":?true,????"declaration":?true,????"outDir":?"./lib",????"allowJs":?true,????"moduleResolution":?"Node"??}}

创建源码文件夹src,添加入口文件index.ts

创建文件夹src,作为源码文件夹,并在其中添加入口文件index.ts。做完这些事情,我们的文件夹看起来应该是这样:

mf2e-test??|-?src????|-?index.ts??|-?.babelrc??|-?package.json??|-?rollup.config.js??|-?tsconfig.json

这时候我们可以安装框架逻辑需要的依赖了,执行下面命令:

yarn?add?@satumjs/core?@satumjs/simple-midwares?@satumjs/midware-single-spa

我们可以测试下es6的源码是否能够正常构建出es5的代码,当然在此之前package.json里要添加一些scripts

"scripts":?{??"build":?"rimraf?lib?&&?rollup?-c",??"build:w":?"npm?run?build?--?-w",??"pub":?"npm?run?build?&&?npm?publish?--access?public"},

之后请在index.ts里添加如下代码,然后执行命令yarnbuild可以看到能正常生成代码。

import?{?register,?start?}?from?'@satumjs/core';export?{?register,?start?};

那么到此,恭喜你,初始化工作告一段落,我们提交一下代码。

添加必要的中间件

@satumjs/simple-midwares是一些中间件的集合,包含了常用的中间件如缓存、沙箱、css隔离、获取挂载dom、路由协调等。我们可以先使用其中的沙箱、获取挂载dom这两个中间件。然后我们还要使用@satumjs/midware-single-spa中间件协助管理微应用的加载和卸载(你可以不使用该中间件,内置有实现简单的single-spa功能,不过强烈建议使用)。index.ts修改代码如下:

import?{?register,?start,?use?}?from?'@satumjs/core';import?{??simpleSandboxMidware,??mountNodeMidware,}?from?'@satumjs/simple-midwares';import?singleSpaMidware?from?'@satumjs/midware-single-spa';use(simpleSandboxMidware);use(mountNodeMidware);use(singleSpaMidware);export?{?register,?start?};

做完这一步,其实我们可以执行一下yarnlink,搞个本地示例工程出来直接看该框架的实际运行情况了。本地示例工程请参考https://stackblitz.com/edit/github-gacap7,换一下依赖@satumjs/core为mf2e-test即可。

添加微应用本地调试工具

所谓调试工具,其本质是获取到微应用的配置后,如果调试whiteList里包含该微应用,则使用whiteList里的entry地址,而这个地址可以指向本地启动的微应用。所以我们继续在index.ts里添加逻辑,我以whiteList存储在localStorage里为例。

use((system,?microApps,?next)?=>?{??system.set(MidwareName.proxyEntry,?(entry:?string?|?string[],?appName:?string)?=>?{????const?proxyMap?=?{};????const?proxySetting?=?localStorage.getItem('proxyEntries')?||?'';????const?proxyData?=?proxySetting.replace(/\s/g,?'').split(',');????proxyData.forEach((item)?=>?{??????const?[itemAppName,?proxyUrl]?=?item.split('|');??????if?(itemAppName?&&?proxyUrl)?proxyMap[itemAppName]?=?proxyUrl;????});????const?currentApp?=?microApps.find((item)?=>?item.name?===?appName);????return?currentApp?&&?proxyMap[appName]???proxyMap[appName]?:?entry;??});??next();});

这样你往浏览器的localStorage里添加形如micro-name|micro-entry-url规则,当框架加载微应用时就会使用本地启动的。多个可以以,隔开。

添加跨域服务,以方便远程非跨域微应用集成

好多时候,我们把微应用部署到开发、测试、预发等环境,可能是直接部署到了cdn上。这些环境并不支持跨域,进而影响集成。我们可以自己搞个跨域服务出来,做一下中转,然后方便微应用集成。框架层面上也要做下支持,其实本质是遇到微应用的entry,为其加一层跨域中转请求。

{"presets":?[?"@babel/typescript",?"@babel/preset-env"]}0

代码编写到这个地方,恭喜你,微前端框架部分就完成了。不过作为框架,需要让用户可以传一些参数进来,控制一些中间件的行为。整体代码如下:

{"presets":?[?"@babel/typescript",?"@babel/preset-env"]}1

微前端框架示例仓库请访问https://github.com/satumjs/mf2e-test

创建配置中心

最简单的是,发一个json文件到cdn。当然可以使用nodejs搭建一个可视化编排json的配置中心出来,这样可以做到不用修改代码,即可上下线微应用。

物料市场

可以使用业界开源的x-design来做基础组件库,根据业务定制一些业务组件出来。这些组件可以在微应用里通过共享,分发到其他子应用。

低代码平台

需要自行实现该平台,然后基于satumjs强悍的集成能力,把分散的页面集成到一个网站下。

后续

本文通过代码的方式一步步定制出一套微前端框架(示例仓库https://github.com/satumjs/mf2e-test),然后又把该框架周边最常用的工具的实现展现了出来。还有一些工程板块,可以自行搭建,也可以使用公司或开源的。如果大家对Satum感兴趣,欢迎访问其官网https://satumjs.github.io/website了解更多。如果对中间件和插件感兴趣,官网上也有一些资料。建议大家加微信群或钉钉群,方便后续交流。感谢大家耐心阅读这篇文章~

原文:https://juejin.cn/post/7099586151291289614