由于最近项目中有可能会使用其他前端架构,因此提前了解了些跟这方面挂钩的一些东西,如何兼容使用多个前端架构,这就出现了一种新的架构方法:微前端。有点类似后端的微服务一样,把一个大型项目拆开成个各个服务去运行
1、背景
微前端(Micro Frontends)是一种将前端应用划分为独立、可组合的子应用的架构方法。这种方法允许开发团队采用不同的技术栈、框架和工具,同时保持应用的整体一致性。微前端的核心理念是将微服务概念应用于前端开发,实现对前端应用模块化和解耦的优势。简单理解可以合在一起组合一个大型综合应用,也可以拆开后单独应用
2、优点
- 提高开发效率:团队可以独立开发、测试和部署各个子应用。
- 技术栈无关:每个子应用可以使用适合其需求的技术栈,无需与其他子应用保持一致。
- 可扩展性:子应用可以轻松添加或删除,整个系统的复杂性得到有效控制。
3、常用解决方案
- Single-spa: 是一个用于将多个独立的应用组合成一个整体应用的JavaScript框架。它允许开发者使用不同的技术栈创建子应用,并将它们组合成完整的应用。
Github仓库: single-spa - 乾坤(qiankun) : 由蚂蚁金服开源的一个基于single-spa的微前端实现方案。它提供了更丰富的功能,包括沙箱隔离、样式隔离等。
Github仓库: qiankun - 代理方案: 可以使用相关的代理方案,通过路由进行区分不同的应用。但是不同应用中的切换,会有一个闪一下的效果。 文档地址: Webpack Module Federation 文档地址: nginx
- iframe:如果是不同源的子应用的话,就需要配置代理,否则消息通信就存在问题
- 以及其他大厂的微前端方案:京东的Micro App、字节跳动Garfish、SAP的Liugi等
4、入门案列
- 1、Single-spa
文档地址: # single-spa-vue
$ git clone https://github.com/joeldenning/coexisting-vue-microfrontends.git
# First terminal tab
$ cd root-html-file
$ npm install
$ npm run serve
# Second terminal tab
$ cd app1
$ npm install
$ npm run serve
# Third terminal tab
$ cd app2
$ npm install
$ npm run serve
# Fourth terminal tab
$ cd navbar
$ npm install
$ npm run serve
关键主应用代码:
{
"imports": {
"navbar": "http://localhost:8080/js/app.js",
"app1": "http://localhost:8081/js/app.js",
"app2": "http://localhost:8082/js/app.js",
"single-spa": "https://cdnjs.cloudflare.com/ajax/libs/single-spa/4.3.7/system/single-spa.min.js",
"vue": "https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js",
"vue-router": "https://cdn.jsdelivr.net/npm/vue-router@3.0.7/dist/vue-router.min.js"
}
}
singleSpa.registerApplication(
'navbar',
() => System.import('navbar'),
location => true
);
singleSpa.registerApplication(
'app1',
() => System.import('app1'),
location => location.pathname.startsWith('/app1')
)
singleSpa.registerApplication(
'app2',
() => System.import('app2'),
location => location.pathname.startsWith('/app2')
)
singleSpa.start();
关键子应用代码:
//app2/src/main.js
import './set-public-path'
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import singleSpaVue from 'single-spa-vue';
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
render: (h) => h(App),
router,
},
});
export const bootstrap = vueLifecycles.bootstrap;
export const mount = vueLifecycles.mount;
export const unmount = vueLifecycles.unmount;
其他案例:single-spa其他例子
- 2、乾坤(qiankun)
$ git clone https://github.com/umijs/qiankun.git
$ cd qiankun
$ yarn install
$ yarn examples:install
$ yarn examples:start
关键主应用代码:
//在主应用入口:qiankun/examples/main/index.js下设置,主应用可以是react、vue或者其他第三方架构
//通过对registerMicroApps,对各种框架应用进行设置入口和拦截
/**
* Step1 初始化应用(可选)
*/
render({ loading: true });
const loader = (loading) => render({ loading });
/**
* Step2 注册子应用
*/
registerMicroApps(
[
{
name: 'react16',
entry: '//localhost:7100',
container: '#subapp-viewport',
loader,
activeRule: '/react16',
},
{
name: 'react15',
entry: '//localhost:7102',
container: '#subapp-viewport',
loader,
activeRule: '/react15',
},
{
name: 'vue',
entry: '//localhost:7101',
container: '#subapp-viewport',
loader,
activeRule: '/vue',
},
{
name: 'angular9',
entry: '//localhost:7103',
container: '#subapp-viewport',
loader,
activeRule: '/angular9',
},
{
name: 'purehtml',
entry: '//localhost:7104',
container: '#subapp-viewport',
loader,
activeRule: '/purehtml',
},
{
name: 'vue3',
entry: '//localhost:7105',
container: '#subapp-viewport',
loader,
activeRule: '/vue3',
},
],
{
beforeLoad: [
(app) => {
console.log('[LifeCycle] before load %c%s', 'color: green;', app.name);
},
],
beforeMount: [
(app) => {
console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
},
],
afterUnmount: [
(app) => {
console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
},
],
},
);
const { onGlobalStateChange, setGlobalState } = initGlobalState({
user: 'qiankun',
});
onGlobalStateChange((value, prev) => console.log('[onGlobalStateChange - master]:', value, prev));
setGlobalState({
ignore: 'master',
user: {
name: 'master',
},
});
/**
* Step3 设置默认进入的子应用
*/
setDefaultMountApp('/react16');
/**
* Step4 启动应用
*/
start();
runAfterFirstMounted(() => {
console.log('[MainApp] first app mounted');
});
关键子应用代码:
// 在子应用的入口qiankun/examples/vue/src/main.js
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
console.log('[vue] props from main framework', props);
storeTest(props);
render(props);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
router = null;
}
5、qiankun 和 single-spa 差异性
qiankun是基于single-spa封装,在single-spa基础上,又提供了一些额外的功能:
- 样式隔离,确保微应用之间样式互相不干扰。
- JS 沙箱,确保微应用之间 全局变量/事件 不冲突。
- 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。