乾坤
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
微前端架构具备以下几个核心价值:
-
技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权 -
独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 -
增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
-
独立运行时
每个微应用之间状态隔离,运行时状态不共享
安装
yarn add qiankun || npm i qiankun -S
主应用注册
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'react app', // app name registered
entry: '//localhost:7100',
container: '#yourContainer',
activeRule: '/yourActiveRule',
},
{
name: 'vue app',
entry: { scripts: ['//localhost:7100/main.js'] },
container: '#yourContainer2',
activeRule: '/yourActiveRule2',
},
]);
start();
App.vue
<template>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/c1/">c1--Home</router-link> |
<router-link to="/c1/about">c1--About</router-link>
</nav>
<router-view/>
<div id="c1Container"></div>
</template>
注册子应用
src/public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
main.js
import './public-path';
import { createApp } from 'vue'
import App from './App.vue'
import routes from './router'
import { createRouter, createWebHistory } from 'vue-router';
import store from './store'
let router = null;
let instance = null;
let history = null;
function render(props = {}) {
const { container } = props;
let arr = routes;
console.log(arr)
history = createWebHistory(window.__POWERED_BY_QIANKUN__ ? '/c1' : '/');
router = createRouter({
history,
routes: routes,
});
instance = createApp(App);
instance.use(router);
instance.use(store);
instance.mount(container ? container.querySelector('#app') : '#app');
}
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log('%c%s', 'color: green;', '微应用初始化vue3.0 app bootstraped');
}
function storeTest(props) {
props.onGlobalStateChange &&
props.onGlobalStateChange(
(value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev),
true,
);
props.setGlobalState &&
props.setGlobalState({
ignore: props.name,
user: {
name: props.name,
},
});
}
export async function mount(props) {
storeTest(props);
render(props);
instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange;
instance.config.globalProperties.$setGlobalState = props.setGlobalState;
}
export async function unmount() {
instance.unmount();
instance._container.innerHTML = '';
instance = null;
router = null;
history.destroy();
}
vue.config.js
const { name } = require('./package');
module.exports = {
devServer: {
port: 7100, // 启动项目时的端口号
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
chunkLoadingGlobal: `webpackJsonp_${name}`,
},
},
};
至此 分别run 父子应用即可看到运行正常 符合预期
业务场景中 通信是不可避免的
父子应用之间相互通信
initGlobalState(state)
-
参数
- state -
Record<string, any>- 必选
- state -
-
用法
定义全局状态,并返回通信方法,建议在主应用使用,微应用通过 props 获取通信方法。
-
返回
-
MicroAppStateActions
- onGlobalStateChange:
(callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) => void, 在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback - setGlobalState:
(state: Record<string, any>) => boolean, 按一级属性设置全局状态,微应用中只能修改已存在的一级属性 - offGlobalStateChange:
() => boolean,移除当前应用的状态监听,微应用 umount 时会默认调用
- onGlobalStateChange:
-
示例
主应用创建 actions.js
import { initGlobalState } from 'qiankun'
const initState = {
username:"古力娜扎"
}
const actions = initGlobalState(initState)
actions.onGlobalStateChange((state, preState) => {
console.log(preState, '主应用变更前');
console.log(state, '主应用变更后');
})
export default actions;
main.ts 注册引入 通过props传入子应用
子应用新建actions.js
function emptyAction() {
// 提示当前使用的是空 Action
console.warn("Current execute action is empty!");
}
class Actions {
// 默认值为空 Action
actions = {
onGlobalStateChange: emptyAction,
setGlobalState: emptyAction,
};
/**
* 设置 actions
*/
setActions(actions) {
this.actions = actions;
}
/**
* 映射
*/
onGlobalStateChange() {
return this.actions.onGlobalStateChange(...arguments);
}
/**
* 映射
*/
setGlobalState() {
return this.actions.setGlobalState(...arguments);
}
}
const actions = new Actions();
export default actions;
main.js mount生命周期中设置接收
子应用业务具体使用
<template>
<h1>c1----首页---{{ state.username }}</h1>
<button @click="change">换一个</button>
</template>
<script>
import actions from "./../qiankun/actions";
export default {
data() {
return {
state:{}
};
},
mounted() {
// 接收state
actions.onGlobalStateChange((state) => {
console.log("state",state);
this.state = state
}, true);
},
methods
change() {
// 修改state
actions.setGlobalState({ username:"热依扎" });
},
},
};
</script>
效果
生产
主应用
子应用
nginx
server {
listen 8080;
server_name 121.40.141.26;
location / {
root /www/server/nginx/html/dist/dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /child/menus {
root /www/server/nginx/html/dist/dist;
error_log /www/server/nginx/logs/error.log debug;
index index.html index.htm;
try_files $uri $uri/ /child/menus/index.html;
}
}
include /www/server/panel/vhost/nginx/*.conf;
}