1.主应用(基座)
在主应用中需要注册微应用的配置文件
// src/App.vue:
<template>
<div id="app">
<img src="./assets/logo.png">
<!-- <router-view/> -->
<p>当前页面在主应用(基座)</p>
<!-- <router-link to="/oms-admin/">oms-admin应用入口</router-link>
<router-link to="/config-admin/">config-admin应用入口</router-link> -->
<ul>
<li class="route_item"
@click="goto('/sub-vue')">微应用1的入口</li>
<li class="route_item"
@click="goto('/sub-react')">微应用2的入口</li>
</ul>
<div id="app-viewport"></div> <!-- 主应用挂载子应用容器的元素节点 -->
</div>
</template>
<script>
export default {
name: 'App',
methods: {
goto(route) {
history.pushState(null, route, route);
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
// src/micro-app.js:
const microApps = [
{
name: 'sub-vue', // 微应用名称;
// entry: process.env.VUE_APP_OMS_ADMIN, // 微应用入口;
entry: 'http://localhost:8081',
activeRule: '/sub-vue', // 微应用激活规则;
},
{
name: 'sub-react',
// entry: process.env.VUE_APP_CONFIG_ADMIN,
entry: 'http://localhost:8082',
activeRule: '/sub-react',
}
];
const apps = microApps.map(item => {
return {
...item,
container: '#app-viewport', // 微应用的容器节点的选择器或者Element实例;
props: { // 主应用需要传递给微应用的数据;
routerBase: item.activeRule // 下发基础路由;
}
}
});
export default apps;
在主应用的入口文件 main.js 文件中注册微应用
// src/main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import microApps from './micro-app';
import { registerMicroApps, start } from 'qiankun';
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
registerMicroApps(microApps, {
beforeLoad: app => {
console.log(`app`, app.name);
},
beforeMount: [
app => {
console.log(`app`, app.name);
}
],
afterMount: [
app => {
console.log(`app`, app.name);
}
],
afterUnmount: [
app => {
console.log(`app`, app.name);
}
],
});
start();
当微应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。
2.微应用(子应用)
在微应用入口文件 main.js 文件(通常就是你配置的 webpack 的 entry js)中导出bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。
// src/main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
let vueInstance = null;
function render(props = { }) {
const { container } = props;
vueInstance = new Vue({
// el: '#app',
router,
components: { App },
template: '<App/>'
}).$mount(container ? container.querySelector('#sub_app') : '#sub_app');
}
if(!window.__POWERED_BY_QIANKUN__) {
render();
}
/**
* 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用mount钩子,不会再重复触发
* bootstrap。通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用
* 级别的缓存等。
*/
export async function bootstrap() {
console.log('子应用 sub: bootstrap ~');
}
/**
* 应用每次切出/卸载会调用的方法,通常在这里我们会卸载微应用的应用实例。
*/
export async function unmount() {
vueInstance = null
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法。
*/
export const mount = async props => {
render(props);
}
// index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>sub-vue</title>
</head>
<body>
<div id="sub_app"></div>
<!-- built files will be auto injected -->
</body>
</html>
3.子应用 webpack 配置
// build/webpack.dev.conf.js:
devServer: {
...
headers: {
'Access-Control-Allow-Origin': '*' // 配置允许跨域访问子应用
}
...
}
// webpack.base.conf.js:
const { name } = require('../package.json');
output: {
// 把子应用打包成 umd 库格式 (主应用加载子应用需要是 umd 规范的打包后文件)
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
}
4.HTML Entry
5.样式隔离
6.运行时沙箱
7.资源预加载
8.应用间通信
主应用与子应用间通信。
/**
* 主应用
* src/main.js:
*/
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import microApps from './micro-app';
import { registerMicroApps, start, setDefaultMountApp, initGlobalState } from 'qiankun';
const actions = initGlobalState({iptValue: '初始值'})
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
registerMicroApps(microApps, {
beforeLoad: app => {
console.log(`app`, app.name);
},
beforeMount: [
app => {
console.log(`app`, app.name);
}
],
afterMount: [
app => {
console.log(`app`, app.name);
}
],
afterUnmount: [
app => {
console.log(`app`, app.name);
}
],
});
// 注册一个观察者函数;
actions.onGlobalStateChange((state, prev) => {
console.log('main-app onGlobalStateChange', state, prev)
});
// 设置 iptValue 的值;
setTimeout(() => {
actions.setGlobalState({ iptValue: '改变了值' });
}, 100);
start();
/**
* 子应用
* src/main.js:
*/
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
Vue.config.productionTip = false
let vueInstance = null;
function render(props = { }) {
console.log(787878, props);
const { container } = props;
vueInstance = new Vue({
// el: '#app',
router,
components: { App },
template: '<App/>'
}).$mount(container ? container.querySelector('#sub_app') : '#sub_app');
}
if(!window.__POWERED_BY_QIANKUN__) {
render();
}
/**
* 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount钩子,不会再重复触发
* bootstrap。通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用
* 级别的缓存等。
*/
export async function bootstrap() {
console.log('子应用 sub: bootstrap ~');
}
/**
* 应用每次切出/卸载会调用的方法,通常在这里我们会卸载微应用的应用实例。
*/
export async function unmount() {
vueInstance = null
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法。
*/
export const mount = async props => {
props.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log('子应用获取的数据', state, prev);
});
props.setGlobalState({ iptValue: '子应用中修改值' });
render(props);
}