Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说微前端架构调研,希望能够帮助你!!!。
微前端应用于浏览器端,主要是对web应用进行拆解,最后将不同子系统(模块)聚合成一个完整的应用.
微前端主要目的是聚合,即将不同子系统聚合成一个大系统,而微服务架构目前更多是解耦,即解耦不同服务间的依赖.
当我们的系统中有多个不同的子模块,并且子模块之间有相对独立且完整的功能体系时, 一旦子模块变得越来越多, 那么整个应用将变得非常庞大且臃肿,开发和维护成本大大提高.如果在这种场景下我们采用SPA模式开发,那么项目后期将变得不可想象,页面首次加载将变得非常慢(可能会遇到这种情况,开发一个复杂系统,页面首次加载花了20多秒,所以不得不采用MPA来处理)。
我们采用MPA(多页应用)模式,虽然解决了应用臃肿的问题, 但仍然存在很多有待处理问题,比如模块切换需要重新刷新页面, 公共组件无法共享,子模块直接,父子模块之间的通信问题,开发部署繁琐等.这写都是传统开发模式会遇到的问题.
试想一下,如果面对以上问题, 如果有一种架构模式, 可以让我们在主应用中共享公共组件和状态(但是要保证子应用运行时内部的状态隔离), 并且不同子模块之间可以单独开发部署, 模块间切换不刷新页面, 并且模块之间,父子应用之间可以通过某种简单的方式实现通信,这样是不是就完美了?不错, 这也许就是微前端要解决的问题.
往往企业的中后台系统更加适合使用微前端架构,因为B端大部分都是业务驱动,而往往这些业务之间会非常复杂, 比如Saas, Paas等系统.像类似于云平台,CRM,ERP系统往往是微前端架构的拯救对象.
1、基于MPA + iframe + Webpack实现的微前端架构
不同子系统之间可以各自维护,单独打包部署,最后通过node或者nginx做路由分发。我们采用公共的ui组件库和js类库来抽离公共组件,但是前提是不同组件库和技术栈强相关,如果没有历史遗留的新项目,建议采用一致的技术栈。
这种方案的缺点就是组件库只能复用而无法真正共享,并且切换路由会导致页面重新渲染刷新。父子系统通信困难,仍然需要iframe最为容器来通信。
2、基umi + qiankun实现的微前端架构
目前市面上也有非常成熟的微前端架构方案,比如single-spa,之前的美团外卖微前端架构和蚂蚁金服基于single-spa开发的微前端架构qiankun(乾坤),都是非常不错的方案。
qiankun主要采用HTML Entry模式,直接将子应用打出来 HTML作为入口,主框架可以通过 fetch html 的方式获取子应用的静态资源,同时将 HTML document 作为子节点塞到主框架的容器中。这样不仅可以极大的减少主应用的接入成本,子应用的开发方式及打包方式基本上也不需要调整,而且可以天然的解决子应用之间样式隔离的问题。
其方案具有如下特点:
支持子应用并行
支持js沙箱环境(js隔离)
css隔离
HTML Entry,简化开发者使用
按需加载
公共依赖加载
父子应用通信机制
子应用嵌套
前端项目基于umi生态开发构建+qiankun(umi--->webpack也可)
3、具体实现
接下来具体介绍如何使用乾坤来搭建我们的微前端架构,由于我们内部采用umi,所以这里我们直接使用其提供的@umijs/plugin-qiankun插件来实现。(好处就是改动成本几乎为零) 首先我们来实现这样一个场景:我们有一个主应用作为基座工程,然后有3个子系统,他们是独立创建维护的,可以采用不同的git仓库来管理。当我们在主应用中切换路由时会切换到对应的子系统,且不刷新页面,完全的SPA体验,接下来我们来具体实现一下吧。
创建工程
mkdir mainSystem subSystemA subSystemB subSystemC// 分别进入各系统目录下,执行以下命令创建我们的项目yarn create umi
2、在各个子系统下安装@umijs/plugin-qiankun插件
yarn add @umijs/plugin-qiankun
复制代码
主应用下的配置// .umirc.js
export default {
plugins: [
[
'@umijs/plugin-qiankun',
{
master: {
// 注册子应用信息
jsSandbox: true, // 是否启用 js 沙箱,默认为 false
prefetch: true, // 是否启用 prefetch 特性,默认为 true
},
},
],
],
};
// app.js
const isDev = process.env.NODE_ENV === 'development'
export const qiankun = {
apps: [
{
mountElementId: 'root-subapp-container', // 洗子应用挂载的节点
name: 'subSystemA', // 唯一 id,和package对应的name字段最好保持一致
entry: isDev ? '//localhost:8001' : '/subSystemA/index.html', // html entry
base: '/subSystemA', 的路由前缀,通过这个前缀判断是否要启动该应用,通常跟子应用的 base 保持一致
history: 'browser', // 子应用的 history 配置,默认为当前主应用 history 配置
},
{
mountElementId: 'root-subapp-container',
name: 'subSystemB',
entry: isDev ? '//localhost:8002' : '/subSystemB/index.html',
base: '/subSystemB',
},
{
mountElementId: 'root-subapp-container',
name: 'subSystemC',
entry: isDev ? '//localhost:8003' : '/subSystemB/index.html',
base: '/subSystemC',
}
],
fetch: url => {
return fetch(url)
}
};
以上是基本的配置,当然我们还可以在app.js里面加入lifeCycles等钩子来控制不同生命周期下的行为。
在子应用中我们同样需要引入@umijs/plugin-qiankun这个插件,具体配置如下:
export default { base: `/${appName}`, // 子应用的 base,默认为 package.json 中的 name 字段 plugins: ['@umijs/plugin-qiankun', { slave: true }],};
基本准备工作已经完成,剩下就是编写业务代码了,我们要想让整个应用一起跑起来,需要先启动基座工程,然后再启动对应的子工程(当然他们可以单独运行)。
但是值得注意的是我们在开发环境中采用devServer提供的带来才能跨域抓取资源,如果应用发布到线上,如果不同子应用采用不同域名,我们还需要解决跨域问题(跨域解决的方案及安全机制也有很多,已经不再是个问题)。实际开发环境我们需要考虑的问题还有很多,这里只做简单介绍,不过根据官方提供的api基本上可以满足大部分的需求场景,所以还是非常值得一试的。笔者后期也会写一个微前端的实际案例发布到github上,可以一起交流学习。
原文链接:https://blog.csdn.net/KlausLily/article/details/105304444
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。