qiankun是一个微前端js框架
注意我们只需要在主应用中安装qiankun,子应用中是不用的。
$ yarn add qiankun # 或者 npm i qiankun -S
vue项目在main.js中注册。
import {
registerMicroApps, start } from 'qiankun';
/**
* 路由监听
* @param {*} routerPrefix 前缀
*/
// function genActiveRule(routerPrefix) {
// console.log(location.pathname)
// return location => location.pathname.indexOf(routerPrefix);
// } //activeRule的配置函数,当然我们也可以直接配置字符串和其他规则,具体参考文档
registerMicroApps([
{
name: 'new-project', // 子应用名称
entry: '//localhost:8082', //子应用入口,就是单独访问子应用时的url,这里我把子应用配置在8082端口
container: '#container', // 子应用挂载的dom容器,我们把它写在主应用的app.vue中
// activeRule: genActiveRule("/app1"), //子应用触发路径,这里使用前面配置的函数
activeRule:"/app1" //当我们主应用的url含有app1时会触发子应用加载,为了方便触发,建议主应用路由使用history模式,在hash模式下,自动添加的#号会影响子应用触发(不知道为什么)
},
]);
//启动
start();
增加挂载子应用的盒子
app.vue
<template>
<div id="app">
<p>我是父应用</p>
<!-- 子应用容器 -->
<div id="container"></div>
</div>
</template>
微应用不需要额外安装任何其他依赖即可接入 qiankun 主应用。
微应用需要在自己的入口 js 导出 bootstrap
、mount
、unmount
三个生命周期钩子,以供主应用在适当的时机调用。
main.js
import './public-path' // 注意需要引入public-path
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router';
import routes from './router';
let router = null;
let instance = null;
function render(props = {
}) {
const {
container } = props;
router = new VueRouter({
base: '/app1', //这里要和在主应用配置的activeRule一样
mode: 'history',
routes,
});
instance = new Vue({
router,
render: h => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
// webpack打包公共文件路径
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}else{
// 独立运行
render();
}
// 生命周期
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
console.log('[vue] props from main framework', props);
render(props);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
router = null;
}
在上面的main.js中,我们导入的是routes对象,并非router实例
对应router->index.js我们调整为
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
const routerPush = Router.prototype.push
Router.prototype.push = function push (location) {
return routerPush.call(this, location).catch(error => error)
}
const routes= [
{
path: '/',
name: '1',
component: () => import('./views/1.vue')
}
]
export default routes;
除了代码中暴露出相应的生命周期钩子之外,为了让主应用能正确识别微应用暴露出来的一些信息,微应用的打包工具需要增加如下配置:
const {
name } = require('./package');
module.exports = {
devServer: {
// 环境配置
host: 'localhost',
port: 8082,
// https
https: false,
hotOnly: true,
headers: {
//子应用必须支持跨域
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
// 把子应用打包成 umd 库格式(必须)
library: `${
name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${
name}`,
},
},
}
使用 webpack 静态 publicPath 配置:在 mian.js 中引入 public-path.js 文件
;(function () {
if (window.__POWERED_BY_QIANKUN__) {
if (process.env.NODE_ENV === 'development') {
__webpack_public_path__ = `//localhost:8082/`;
return;
}
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
})();
qiankun内部使用
initGlobalState(state)定义全局状态,该方法执行后返回一个
MicroAppStateActions实例,实例中包含三个方法,分别是
onGlobalStateChange、
setGlobalState、
offGlobalStateChange。
默认会通过props
将通信方法传递给子应用。
MicroAppStateActions
(callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) => void
, 在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback(state: Record) => boolean
, 按一级属性设置全局状态,微应用中只能修改已存在的一级属性(就是用来修改全局状态的() => boolean
,移除当前应用的状态监听,微应用 umount 时会默认调用理解:这里的全局状态和vuex中的state十分类似,不同的是全局状态可以在主应用和微应用中共享
新建src->actions.js
import {
initGlobalState } from "qiankun";
import store from "./store";
const initialState = {
//这里写初始化数据
};
// 初始化 state
const actions = initGlobalState(initialState);
actions.onGlobalStateChange((state, prev) => {
//监听公共状态的变化
console.log("主应用: 变更前");
console.log(prev);
console.log("主应用: 变更后");
console.log(state);
store.commit('setProject',state);//这里我把公共状态存到主应用的vuex里了
});
export default actions;
在组件中使用actions
tab.vue
<template>
<div>
<button @click="sendMes1">点击向子应用发送消息1</button>
<button @click="sendMes2">点击向子应用发送消息2</button>
<p>当前显示的项目: {
{
project}} </p>
</div>
</template>
jindei
<script>
import actions from './actions'//记得导入actions实例
export default {
data() {
return {
mes1: {
project_id: '项目1' },
mes2: {
project_id: '项目2' },
}
},
computed:{
project:function(){
return this.$store.state.project_id
}
},
methods: {
sendMes1() {
actions.setGlobalState(this.mes1);//通过setGlobalState改变全局状态
},
sendMes2() {
actions.setGlobalState(this.mes2);
}
},
}
</script>
新建src->actions.js
function emptyAction() {
//设置一个actions实例
// 提示当前使用的是空 Action
console.warn("Current execute action is empty!");
}
class Actions {
// 默认值为空 Action
actions = {
onGlobalStateChange: emptyAction,
setGlobalState: emptyAction,
};
/**
* 设置 actions
*/
setActions(actions) {
this.actions = actions;
}
/**
* 映射
*/
onGlobalStateChange(...args) {
return this.actions.onGlobalStateChange(...args);
}
/**
* 映射
*/
setGlobalState(...args) {
return this.actions.setGlobalState(...args);
}
}
const actions = new Actions();
export default actions;
main.js
在mounted的生命周期里注入actions实例
export async function mount(props) {
actions.setActions(props); //注入actions实例
render(props);
}
在vue 组件中使用
1.vue
<template>
<div>
<div>这是子应用</div>
<p>接收到的消息: {
{
mes}}</p>
<button @click= "butClick">点击向父应用发送消息</button>
</div>
</template>
<script>
import actions from '../actions'//导入实例
export default {
data() {
return {
mes: '',
}
},
mounted() {
actions.onGlobalStateChange((state) => {
//监听全局状态
this.mes = state
}, true);
},
methods:{
butClick(){
actions.setGlobalState({
project_id: '项目99'})//改变全局状态
}
}
}
</script>