简介
微前端基于后台微服务衍生的前端应用架构,是将一个项目按不同模块、不同功能分解成能够独立开发、测试、部署的子项目,最后再合并运行
qiankun是阿里基于single-spa开发的微前端框架,功能更完善
使用场景
- 同一个项目不同团队使用不同的技术栈
- 旧项目的迁移,需要保留部分旧代码
- 相同模块(用户管理)不同项目反复使用
优点
子应用高度自治,独立开发、独立部署、独立运行
无关技术栈(子应用可以是react、vue、anglar、html)
可实现业务解耦(将大应用拆解成多个小应用)
依赖资源复用
补充:与iframe相比
iframe虽然可内嵌网页,但是iframe有很多缺点
- 无法感知url状态,比如后退
- ui不同步,dom不共享
- 页面通信麻烦
- 加载效率慢
快速搭建
一、主应用配置
1、创建主应用(主应用可以是任意技术栈)
2、主应用引入qiankun 插件
// npm
npm install qiankun --save
// yarn
yarn add qiankun
说明:子应用不需要安装qiankun插件
3、主应用的 main.js配置qiankun
注册子应用,并启动
import { registerMicroApps, start ,initGlobalState} from 'qiankun'
// 1、传参数
const { onGlobalStateChange } = initGlobalState({user: 'zhangsan'}) // 全局状态user赋值“zhangsan”
// 监听全局状态,若子应用修改了,此处也会相应获取到修改后的值
onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log('主应用state', state, prev)
},true)
// 2、注册子应用
registerMicroApps([
{
name: 'vueApp', // 应用名称
entry: '//localhost:7101', // 子项目的域名、端口
container: '#vueApp', // 容器名(子项目的容器id)
activeRule: '/vue', // 激活路径
props: { a: 1 } // 第二种传参:传递的值(可选)
}
//......子应用2
//......子应用3
], {
beforeLoad: [
app => {
// 挂载前回调
}
],
beforeMount: [
app => {
// 挂载后回调
}
],
afterUnmount: [
app => {
// 卸载后回调
}
]
})
// 3、启动
start()
4、主应用的 App.vue添加元素
<template>
<div id="app">
<button @click="$router.push('/vue/XX1')">子应用页面1</button>
<button @click="$router.push('/vue/XX2')">子应用页面2</button>
<router-view></router-view>
<!-- 此处需要添加一个id为vueApp的元素,和注册的子应用container一致 -->
<div id="vueApp"></div>
</div>
</template>
二、子应用配置
1、创建多个子应用(子应用也可以是任意技术栈)
2、子应用的 main.js配置qiankun
let router = null;
let instance = null;
function render(props = {}) {
const { container } = props;
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/vue' : '/', //根据是否挂载来动态切换根目录
mode: 'history',
routes,
});
instance = new Vue({
router,
store,
render: h => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
// 在主应用id为vueApp的元素下会生成一个容器,容器里面包含子应用的所有元素
// 若是挂载主应用运行,需要容器去查找子应用根节点id为app的元素,若直接使用#app会与主应用的id为app的元素冲突,所以有此写法,当然子应用若取不同id则可直接$mount('#childId')
}
if (window.__POWERED_BY_QIANKUN__) { // 动态添加publicPath
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// 默认独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
function storeTest(props) {
// 方法一:onGlobalStateChange(callback,fireImmediately) 监听全局状态的变化
// callback 回调函数 state: 变更后的状态; prev 变更前的状态
// fireImmediately Bloolean类型,设置状态是否立马更新监听
props.onGlobalStateChange &&
props.onGlobalStateChange(
(state, prev) => console.log(`子应用监听state:`, state, prev),
true,
);
// 方法二:setGlobalState 更新全局状态的数据,并触发全局监听
props.setGlobalState &&
props.setGlobalState({
user:props.name, // 将当前子应用的name赋值给user
});
}
// 父应用加载子应用,子应用必须暴露三个接口:bootstrap、mount、unmount
// 加载前
export async function bootstrap() {}
// 加载后
export async function mount(props) {
storeTest(props);
render(props);
}
// 销毁后
export async function unmount() {
instance.$destroy();
instance = null;
router = null;
}
3、子应用的 Vue.config.js配置允许跨域
const { name } = require('./package');
module.exports = {
devServer: {
port: 7101,//这里的端口是必须和父应用配置的子应用端口一致
headers: {
//因为qiankun内部请求都是fetch来请求资源,所以子应用必须允许跨域
'Access-Control-Allow-Origin': '*'
}
},
configureWebpack: {
output: {
// 把子应用打包成 umd 库格式
library: `${name}-[name]`,
libraryTarget: 'umd'
}
}
最后启动项目(主项目、子项目都要启动运行)
补充说明:
(1)生命周期执行顺序
主应用beforeLoad()→子应用bootstrap()→主应用beforeMount()→子应用mount()→子应用unmount()→主应用afterUnmount()
(2)应用间通信
一是注册子应用时传递props,单向传递
二是使用全局状态GlobalState,子应用可更改