话不多说,直接上货
首先创建两个项目,一个作为项目基座(vue-qiankun-base),一个作为子应用(vue-qiankun-app1)
搭建基座及配置
安装qiankun依赖到基座
npm i qiankun -S
需要注意的几点:
1.新增 public-path.js 文件,用于修改运行时的 publicPath 什么是运行时的 publicPath ?
2.微应用建议使用 history 模式的路由,需要设置路由 base,值和它的 activeRule 是一样的。
3.在入口文件最顶部引入 public-path.js ,修改并导出三个生命周期函数。
4.修改 webpack 打包,允许开发环境跨域和 umd 打包。
配置基座
1.注册微应用并启动:
在mani中导入并启用
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'app1',
entry: '//localhost:3000',
container: '#app1',// 子应用的id
activeRule: '/app1',
},
]);
// 启动 qiankun
start();
在app中加入相对应的app1
2.基座添加路由
router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
const routes = [];
const router = new VueRouter({
mode: 'history',
routes,
});
export default router
main.js中导入并挂载到vue的实例上
import router from './router';
new Vue({
router,
render: h => h(App)
}).$mount('#app')
路由跳转 >>> 因为只是介绍使用方式,故就在App.vue中写入跳转方式
Cat
Dog
vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
username: ''
},
getters: {
},
mutations: {
changeName(state, username){
state.username = username;
}
},
actions: {
changeName(context, username){
context.commit('changeName', username);
}
},
modules: {
}
})
initGlobalState(state)
定义全局状态,并返回通信方法,建议在基座使用,子应用通过 props 获取通信方法。
MicroAppStateActions:
onGlobalStateChange: (callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) => void:
在当前应用监听全局状态,有变更触发 callback,fireImmediately = true 立即触发 callback
setGlobalState: (state: Record) => boolean:
按一级属性设置全局状态,微应用中只能修改已存在的一级属性
offGlobalStateChange: () => boolean:
移除当前应用的状态监听,微应用 umount 时会默认调用
import { initGlobalState, MicroAppStateActions } from 'qiankun';
// 注意放到start()下面
const state = {
username: '方骈臻'
}
// 初始化 state
const actions: MicroAppStateActions = initGlobalState(state);
actions.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log(state, prev);
});
actions.setGlobalState(state);// 初始化了state
// actions.offGlobalStateChange();
store.dispatch('changeName', state.username);// 修改vuex中值
App.vue中
{{ $store.state }}
基座
(Cat
Dog )
配置子应用
1.在 src 目录新增 public-path.js:(主要作用是补全路径,浏览器有跨域的问题,需要拿到完整的url(特指图片等文件))
/*eslint disable no undef*/
// 上方这一行用于eslint的忽略,因为下方代码的指向其实是不存在的,在有eslint的环境中不加入此行会报错
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
将此文件在main.js头部引入
import './public-path';
2.入口文件 main.js 修改,为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围。
let instance = null;
function render(props = {}) {
const { container } = props;
instance = new Vue({
store,
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
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;
}
3.打包配置修改(vue.config.js):
const { name } = require('./package');
module.exports = {
devServer: {
port: 82,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${name}`,// 取决于webpack的版本(可不要,但小于webpack5.x则需要加上)
},
},
};
此时基础的框架已经搭建完毕,可以显示子应用了,我们再来配置路由
首先是在src下创建router目录,并在其中创建index.js,响应的文件也创建完成
import Vue from "vue";
import Cat from '../pages/Cat';
import Dog from '../pages/Dog';
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/cat',
component: Cat
},{
path: '/dog',
component: Dog
},
];
export default routes
再在main.js中改写为如下
import './public-path';
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'// 引入路由
import routes from './router'// 引入路由文件
Vue.config.productionTip = false
let router = null;// 创建一个空值
let instance = null;
function render(props = {}) {
const { container } = props;
// 给该值赋值。注意,上面提到过,子组件中需使用history模式
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/app1' : '/', // 注意此处的名字要与基座中对应
mode: 'history',
routes,
});
instance = new Vue({
router,// 将路由挂在到vue上
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
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;// 释放路由
}
路由跳转 >>> 因为只是介绍使用方式,故就在App.vue中写入跳转方式
注:子应用中不需要加入/app1的前缀可独立运行
Cat
Dog
vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
username: ''
},
getters: {
},
mutations: {
changeName(state, username){
state.username = username;
}
},
actions: {
changeName(context, username){
context.commit('changeName', username);
}
},
modules: {
}
})
main.js
import store from './store'
let router = null;
let instance = null;
// 调用基座vuex数据的方法
function storeTest(props) {
//props.onGlobalStateChange &&
// props.onGlobalStateChange(
// (value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev),
// true,
//);
props.onGlobalStateChange &&
props.onGlobalStateChange(
(value, prev) => {
store.dispatch('changeName', value.username);
},
true,
);
props.setGlobalState &&
props.setGlobalState({
ignore: props.name,
user:{
name: props.name
}
})
}
function render(props = {}) {
const { container } = props;
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/app1' : '/',
mode: 'history',
routes,
});
console.log(props);// 打印从基座拿到的数据
instance = new Vue({
router,
store, // 一定是挂载到qiankun的方法里面来
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
// 此处使用
export async function mount(props) {
console.log('[vue] props from main framework', props);
storeTest(props)
render(props);
}
App.vue
{{ $store.state }}
子应用
Cat
Dog )
基座与子应用之间的数据互通
基座
main.js
// 如果要在页面中使用,可以挂在到原型链上
// 也可以将需要通信的数据在vuex中进行操作,此处便不再赘述
Vue.prototype.$setGlobalState = actions.setGlobalState;
actions.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log(state, prev);
// 监听子应用数据更改
});
App.vue
子应用
main.js
function storeTest(props) {
// 如果改方法存在,则能拿到对应的数据
// props.onGlobalStateChange &&
// props.onGlobalStateChange(
// (value, prev) => console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev),
// true,
// );
props.onGlobalStateChange &&
props.onGlobalStateChange(
(value, prev) => {
store.dispatch('changeName', value.username);
},
true,
);
// props.setGlobalState &&
// props.setGlobalState({
// ignore: props.name,
// user:{
// name: props.name
// }
// })
if(props.setGlobalState){
Vue.prototype.$setGlobalState = props.setGlobalState; // 同基座,将set方法加载到原型链上
}
}
App.vue
{{ $store.state }}
子应用
(Cat
Dog )