qiankun微前端应用
根据公司项目需要,将老项目的视频监控功能移植到新项目中,正好有机会实践一下qiankun框架。根据官网 (opens new window)介绍,应用qiankun对技术栈没有限制,由于qiankun的Api非常简洁,主应用与子应用进行简单配置,就能完成改造。最后分享一个根据官网搭建的qiankun微应用,实现父应用与子应用的�通信,若是qiankun启动,兄弟应用可以通过修改父应用的全局状态来通信。
项目地址:https://github.com/AlienGao/qiankun-demo (opens new window)
# 主应用配置
main/src/micro-apps.js
import shared from './shared'
const mircoApps = [
{
name: 'react-app',
entry: process.env.REACT_APP_REACT_PATH,
activeRule: '/app-react',
},
{
name: 'vue-app',
entry: process.env.REACT_APP_VUE_PATH,
activeRule: '/app-vue',
}
]
// console.log(process.env, 'vue_path')
const apps = mircoApps.map(app => {
return {
...app,
// 子应用容器
container: '#container',
props: {
// 暴露给子应用的方法
getGlobalState: shared.getGlobalState,
setGlobalState: shared.setGlobalState
}
}
})
export default apps
main/src/shared/index.js
import { initGlobalState } from 'qiankun';
const initialState = { taskList: [] };
const actions = initGlobalState(initialState);
actions.onGlobalStateChange((newState, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log('main change', JSON.stringify(newState), JSON.stringify(prev));
for (let key in newState) {
initialState[key] = newState[key]
}
}, true);
// 定义一个获取state的方法下发到子应用
actions.getGlobalState = (key) => {
// 有key,表示取globalState下的某个子级对象
// 无key,表示取全部
return key ? initialState[key] : initialState
}
// actions.setGlobalState(state);
export default actions;
这两个文件主要配置了子应用的端口,容器和需要用到的方法,通过将setGloablState暴露给子应用,可以做到通过一个子应用修改全局状态再同步给另一个子应用。
最后,在父应用的入口文件注册子应用。
import reportWebVitals from './reportWebVitals';
import { registerMicroApps, start, setDefaultMountApp } from 'qiankun';
import microApps from './micro-apps'
const apps = microApps
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
registerMicroApps(apps, {
beforeLoad: [app => {
console.log(`${app.name}-beforeLoad`)
}],
beforeMount: [app => {
console.log(`${app.name}-beforeMount`)
}],
afterMount: [app => {
console.log(`${app.name}-afterMount`)
}],
beforeUnmount: [app => {
console.log(`${app.name}-beforeUnmount`)
}],
afterUnmount: [app => {
console.log(`${app.name}-beforeUnmount`)
}]
});
setDefaultMountApp('/app-vue')
// 启动 qiankun
start();
上面为效果图,上方为主应用,下方位微应用容器。
# 子应用配置
子应用首先需要在入口文件处配置三个钩子函数用于qiankun识别生命周期。
vue-app/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);
globalRegister(store, props)
render(props);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
router = null;
}
其次在Vue子应用mount时期,通过globalRegister函数初始化了全局状态。
vue-app/src/shared/index.js
/**
*
* @param {vuex实例} store
* @param {qiankun下发的props} props
*/
function registerGlobalModule(store, props = {}) {
if (!store || !store.hasModule) {
return;
}
// 获取初始化的state
const initState = props.getGlobalState && props.getGlobalState() || {};
// 将父应用的数据存储到子应用中,命名空间固定为global
if (!store.hasModule('global')) {
const globalModule = {
namespaced: true,
state: initState,
actions: {
// 子应用改变state并通知父应用
setGlobalState({ commit }, payload) {
commit('setGlobalState', payload);
commit('emitGlobalState', payload);
},
// 初始化,只用于mount时同步父应用的数据
initGlobalState({ commit }, payload) {
commit('setGlobalState', payload);
},
},
mutations: {
setGlobalState(state, payload) {
// eslint-disable-next-line
state = Object.assign(state, payload);
},
// 通知父应用
emitGlobalState(state) {
if (props.setGlobalState) {
// console.log(state, 'emitGlobalState')
props.setGlobalState(state);
}
},
},
};
store.registerModule('global', globalModule);
} else {
// 每次mount时,都同步一次父应用数据
store.dispatch('global/initGlobalState', initState);
}
}
export default registerGlobalModule;
若Vue应用没有global Modal,则根据全局状态创建一个。其中能够通过父应用传递的setGlobalState方法改变父应用状态。
computed: {
taskList() {
return this.$store.state.global.taskList;
}
},
methods: {
onChange(task) {
this.$store.dispatch('global/setGlobalState', task)
// this.microSetGlobalState(this.$store.state.global)
}
},
Vue应用可以通过计算属性获取最新的父应用状态。其中有两种方式修改父应用状态,一种是通过dispatch 'global/setGlobalState' action,另一种是直接通过父应用传递的setGlobalState方法修改,该方法需要在Vue的入口文件中设置Vue实例的全局方法。
vue-app/src/main.js
const { container, getGlobalState } = props;
Vue.prototype.getGlobalState = getGlobalState
点击父应用添加任务,在Vue应用中进行双向绑定。
在Vue子应用中修改任务
能够在兄弟应用中展示最新的任务内容
最后未来让主应用能正确识别微应用暴露出来的一些信息,微应用的打包工具需要增加如下配置。
vue-app/vue.config.js
const { name } = require('./package');
module.exports = {
devServer: {
// open: true,
// hot: true,
// compress: true,
// disableHostCheck: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${name}`,
},
},
};
🎉彩蛋🎉:在项目根目录中配置了npm-run-all的命令,可以很方便的启动主应用与子应用。