iframe
基座(vue)
基座可以放自己路由,以及音乐其他子应用
注册应用(main.js)
- 安装
npm i -S qiankun - 引入qiankun,
import {registerMicroApps,start} from 'qiankun' - 注册应用
const apps = [
{
name: 'vueApp',//应用自定义名称
entry: '//localhost:10000',//默认会加载这个html解析里面的js动态资源(子应用必须解决跨域),使用fetch来请求
container:'#vue',//容器名
activeRule: '/vue'//激活的路径
},
{
name: 'vueApp',//自定义名称
entry: '//localhost:20000',
container:'#react',
activeRule: '/react'
}
]
registerMicroApps(apps);//注册应用
start();//启动应用
加载的为微组件
例如一个路由的弹窗框
import { loadMicroApp } from 'qiankun'
loadMicroApp({
name: 'app',
entry: '//localhost:7100',
container: '#yourContaniner'
})
子项目更改(vue)
路由的base路径
new VueRouter({
mode:'history',
base: '/vue',//更改为对应的子路由路径
//base: window.__POWERED_BY_QIANKUN__?'/vue':'/',//完整写法
routes
})
导出要求的三个方法
导出的方法规定要promise,所以可以使用async直接返回promise
//main.js
...
let instance = null//方便卸装
function render(){
instance = new Vue({
router,
render: h=>h(App)
}).$mount('#app');//挂载到自己的html中,基座会拿到这个挂载后的html再插入到基座中
}
//给webpack的publicpath动态注入基座的路径,主要解决微应用动态载入的脚本、样式、图片等 地址不正确的问题
//添加这个,不然基座触发跳转到子项目路由会有问题
if(window.__POWERED_BY_QIANKUN__){
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
//保证单独项目也能够独立运行,有window.__POWERED_BY_QIANKUN__说明是基座调用
if(!window.__POWERED_BY_QIANKUN__){
render()
}
//方法必须有
//类似于created方法,只在初始化的时候触发
export async function bootstrap(props){}
//
export async function mount(props){
render(props)
}
export async function unmount(props){
instance.$destory()
}
//vue.config.js
module.exports = {
//publicPath: '/',//这里打包地址要基于主应用中注册的entry值
devServer: {
port: 20000,
headers: {
'Access-Control-Allow-Origin': '*'
}
},
configureWebpack: {
output: {
library: 'vueApp',//库名,与主应用注册的微应用的name一致
libraryTarget: 'umd',//用window.vueApp可以拿到main.js暴露的所有东西
}
}
}
启动该项目
应该是该项目单独也能正常访问,基座调用切换到对应的/vue的路由也可以正常显示
注意问题(正式环境下部署)
主应用注册时候的配置
主应用注册微应用时候,entry可以为相对路径,activeRule不可以和entry一样(否则主应用页面刷新就变成微应用)
const apps = [{
name: 'VueApp',
entry: '/system/',//http://localhost/system/ 这里会通过nginx代理指向对应的子应用地址
container: '#frame',
activeRule: '/manage'
}]
vue路由的base
如果是主应用调用的那么路由的base为/manage/
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__?'/manage':'/',
mode: 'history',
routes
})
webpack打包配置
对于webpack构建的微应用,微应用的webpack打包的publicPath需要配置成/system/,否则微应用的inde.html能正常请求,但是微应用index.html里面的js/css路径不会带上/system/.
module.exports = {
publicPath: '/system/''
}
nginx配置
主应用:
/system/代理代理到子应用中,即访问到/system/会代理到子应用的地址才可以访问到子应用
子应用的配置
跨域等配置
dockerfile配置
自动化配置(gitlab-cli/cd配置)
部署逻辑
nginx代理,但不适合自动化部署,最理想的方法为下面:
参考
完整的项目
vue-base(基座代码)
//main.js
import Vue from 'vue'
import App from './App.vue'
import vueRouter from 'vue-router'
import {registerMicroApps,start} from 'qiankun'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
var router = new vueRouter({
mode: 'history',
base: '/',
routes: [
{
path: '/hello',
component: ()=>import('./components/HelloWorld.vue')
}
]
})
Vue.config.productionTip = false
const apps = [
{
name: 'vueDemo',//应用自定义名称
entry: '//localhost:10001',//默认会加载这个html解析里面的js动态资源(子应用必须解决跨域),使用fetch来请求
container:'#vue',//容器名
activeRule: '/vue'//激活的路径
},
{
name: 'vueDemo1',//自定义名称
entry: '//localhost:20000',
container:'#vue1',
activeRule: '/vue1'
}
]
registerMicroApps(apps);//注册应用
start();//启动应用
new Vue({
router: router,
render: h => h(App),
}).$mount('#app')
//App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png">
<router-link to="/hello">hello</router-link>
<router-link to="/vue">vueDemo</router-link>
<router-link to="/vue1">vueDemo1</router-link>
<router-view></router-view>
<div id="vue"></div>
<div id="vue1"></div>
</div>
</template>
<script>
export default {
name: 'App',
}
</script>
子项目vue_demo
//main.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
var instance = null
function render(props){
let { container } = props;
instance = new Vue({
render: h => h(App),
}).$mount(container ? container.querySelector('#app') : '#app')
}
//加这个防止父路由跳子路由出现问题
if (window.__POWERED_BY_QIANKUN__) {
// 动态设置 webpack publicPath,防止资源加载出错
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
if(!window.__POWERED_BY_QIANKUN__){
render({})
}
//方法必须有
//类似于created方法,只在初始化的时候触发
export async function bootstrap(){}
//
export async function mount(props){
render(props)
}
export async function unmount(){
instance.$destroy()
}
//vue.config.js
module.exports = {
devServer: {
port: 10000,
headers: {
'Access-Control-Allow-Origin': '*'
}
},
configureWebpack: {
output: {
library: 'vueDemo',//库名,与主应用注册的微应用的name一致
libraryTarget: 'umd',//用window.vueApp可以拿到main.js暴露的所有东西
}
}
}
vue_demo1配置参考vue_demo
实现效果
- 启动vue_demo和vue_demo1子项目
- 启动主项目,图示:
实现效果为,每个路由都能正确显示每个页面
可能遇到的问题
- 会报
_webpack_public_path__没有定义,原因webpack_public_path 不是全局变量所导致的,子应用 package.json 文件中 eslintConfig 配置全局变量后 重起服务,package.json添加这个全局变量,代码如下:
"eslintConfig": {
...
"globals": {
"__webpack_public_path__ ": true
}
},
如果还是不行,直接去掉eslint:
//vue.config.js
module.exports = {
lintOnSave: false,
....
}
参数传递
qiankun内部使用initGlobalState(state)定义全局状态,该方法执行后返回一个MicroAppStateActions实例,实例中包含三个方法,分别是onGlobalStateChange、setGlobalState、offGlobalStateChange
- 主应用:
//main.js
import {initGlobalState } from 'qiankun'
const actions = initGlobalState({});
Vue.prototype.$actions = actions
//模拟全局token
<button @click="setToken">设置token</button>
methods: {
setToken(){
let token = Math.random()*10000//模拟token
console.log('actions的',this.$actions);
this.$actions.setGlobalState({ token })
}
}
- 子应用获取,子应用在props传递的时候就已经自带了
onGlobalStateChange和setGlobalState这两个方法,只要主应用创建一个MicroAppStateActions,保证所有子应用使用的都是该对象就可以实现数据共享。
//main.js
export async function mount(props){
if (props) {
Vue.prototype.$actions = props
}
render(props)
}
mounted () {
//第二个参数为是否立即触发
this.$actions.onGlobalStateChange((state) => {
this.token = state.token;
}, true);
}