基于Vue-cli3.0的qiankun(乾坤)前端微服务说明

官方文档地址:qiankun.umijs.org/zh

主服务

1.安装乾坤依赖(子服务不需要安装,只需要暴露生命周期方法)

2.在入口文件js中添加如下代码

import {registerMicroApps, start} from "qiankun";
let microProject = [
  {
    //项目名称,应当与子服务中的package.json项目名称一致
    name: 'wdcsLogin app',
    //子服务的入口(可以是js入口,但是这里我们统一使用html入口 也就是项目前端地址)
    entry: 'http://192.XXX.XXX.XXX:8086',
    //子服务的容器dom,实际上是主服务中的某个dom的id,用来装载子服务
    container: '#micro_container',
    //传递给子服务的的子服务ip,用于识别public文件
    //props属性可以用来传递给子服务数据,比如传入公共的vuex实例
    props: {microProjectPath: 'http://192.XXX.XXX.XXX:8086'},
    //子服务的载入路由,实际上在主服务中,qiankun会识别该路由,从而激活子服务
    activeRule: '/loginWdcsFrame'
  },
  {
    name: 'wdcs',
    entry: 'http://192.XXX.XXX.XXX:8086/wdcs.html',
    container: '#micro_container',
    props: {microProjectPath: 'http://192.XXX.XXX.XXX:8086'},
    activeRule: '/wdcsFrame'
  },]
//注册微服务
registerMicroApps(microProject)
//启动!!
start()

微服务

1.需要在入口文件js中添加乾坤生命周期方法

export async function bootstrap() {
    console.log('[vue] vue app bootstraped');
}

export async function mount(props) {
    console.log('[vue] props from main framework', props);
    //storeTest(props);
    // 接收父项目传递过来的public文件路径
    // 因为父项目加载子项目后,public路径会指向父项目
    // 这里父项目加载时,把子项目地址传递过来,似的子项目加载指向自己的public路径
    render(props.microProjectPath + '/json/config.json',props);
}

export async function unmount() {
    //销毁实例
    window.Wdapp.$destroy();
    window.Wdapp.$el.innerHTML = '';
    router = null
}

2.设置publicPath

新建脚本public-path.js脚本并在main.js中引入,这段代码配合vue.config.js的chainWebpack使用,
来解决主服务加载js.css 图片等资源路径404问题。
而在子服务中我们可以使用变量window.__POWERED_BY_QIANKUN__ 来判断是否处于qiankun框架中

if(window.__POWERED_BY_QIANKUN__){
    __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

3.改写vue实例初始化代码为方法

let router = null;
//读取config.json中的配置修改为参数传递,并且挂载的dom也要根据主服务传递的参数判断动态改变
function render(configPath = 'json/config.json',props = {}) {
    console.log(configPath)
    const { container } = props;
    //实例化路由
    router = new Router(routerConfig)
    axios.get(configPath).then(res => {
        store.state.config = res.data;
        axios.defaults.baseURL = res.data.baseUrlsByZhwd;
        //读取token和user信息
        let userInfo = JSON.parse(localStorage.getItem("WdUserInfo"))
        if(!userInfo){
           window.location.href = window.__POWERED_BY_QIANKUN__ ? '/loginWdcsFrame':res.data.redirectWdLoginUrl;
        }
        let token = JSON.parse(localStorage.getItem("WdUserInfo")).token;

        window.userId = JSON.parse(localStorage.getItem("WdUserInfo")).userId;
        if (token) {
            axios.defaults.headers.common['token'] = token;
        }
        let app = new Vue({
            router,
            store,
            render: h => h(App)
            //挂载的dom也要根据主服务传递的参数判断动态改变
        }).$mount(container ? container.querySelector('#wdcsApp') :'#wdcsApp')
        window.app = app
    })
}

4.改写路由暴露方式

原先路由对象实在router.js中进行实例化并暴露的,

export default new Router({
    // activeRule: '/yourActiveRule', 这个必须和主服务的路由一致
     base: window.__POWERED_BY_QIANKUN__ ? '/yourActiveRule' : '/',
    'routes': routers
})

现在修改为只暴露出配置对象

export default {
    // activeRule: '/yourActiveRule', 这个必须和主服务的路由一致
     base: window.__POWERED_BY_QIANKUN__ ? '/yourActiveRule' : '/',
    'routes': routers
}

路由实例在第3点改写的render()方法中进行实例化
这样做的目的是为了能够让路由在qiankun的生命周期方法unmount中进行销毁.
注:路由需要设置base属性,处于qiankun中,设置为与主服务路由一直的前缀,否则将无法正确路由

5.修改 window.location.href代码

子服务项目中所有与 window.location.href 跳转有关的代码必须加入乾坤状态判断,否则跳转404
而主服务中也必须设置相应activeRule属性

//子服务  
  window.location.href = window.__POWERED_BY_QIANKUN__ ? '/wdcsFrame' :_this.$config.redirectWdcsUrl;
//主服务相应设置
  {
      name: 'wdcs',
      entry: 'http://192.XXX.XXX.XXX:8086/wdcs.html',//子服务的入口
      container: '#micro_container',//子服务的容器dom,实际上是主服务中的某个dom的id,用来装载子服务
      props: {microProjectPath: 'http://192.XXX.XXX.XXX:8086'},
      activeRule: '/wdcsFrame'//子服务的载入路由,实际上在主服务中,qiankun会识别该路由,从而激活子服务
  }

6.修改子服务配置文件vue.config.js

(1)子服务允许跨域,否则主服务无法访问

devServer: {
        https: false, //设置前端https进行访问
        port: 8084,
        headers: {
            'Access-Control-Allow-Origin': '*',
        },
    },

(2)修改子服务输出格式
name读取自package.json的项目名称,应当与主服务配置的name属性统一
这么做的目的是为了项目打包后能够暴露出qiankun的生命周期函数给主服务识别

configureWebpack: {
        output: {
            library: `${name}-[name]`,
            libraryTarget: 'umd',
            jsonpFunction: `webpackJsonp_${name}`,
        },
    }

(3)配置静态文件解析loader,否则主服务加载子服务后,字体、图片等会404

/*链式配置,解决qiankun框架加载子服务图标显示方块问题*/
     chainWebpack: config => {
            const imagesRule = config.module.rule('images');
            imagesRule.uses.clear()
            imagesRule.test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
            imagesRule.use('file-loader')
                .loader('url-loader')
                .options({
                    //limit:10000,
                    fallback:{
                        loader:'file-loader',
                        options:{
                            name:'img/[name].[hash:8].[ext]'
                        }
                    }
                })
            const fontsRule = config.module.rule('fonts');
            fontsRule.uses.clear()
            fontsRule. test(/\.(eot|svg|ttf|TTF|woff|woff2?)$/)
            fontsRule.use('file-loader')
                .loader('url-loader')
                .options({
                    fallback:{
                        loader:'file-loader',
                        options:'fonts/[name].[hash:8].[ext]'
                    }
                })
        },

2021/2/26 踩坑记录------子应用多页面时,主应用无法正常路由跳转至其他页面或子应用

这里就不贴代码了
基于webpack构建的多页面应用(以vue-cli3.0为例)
项目基于vue-cli3.0构建的多页面应用,我们通常需要在vue.config.js 之中去配置pages属性来实现多页面,其中有一个filename属性,配置后会改变项目路由的pathname,也就是hash前面的路由。
比如单页面使用hash路由时,我们的地址可能是http://192.168.XX.XX:8080/#/home,而我们配置了多页面后,假设我们的filename为‘index.html’,那么url访问地址就是http://192.168.XX.XX:8080/index.html#/home

以上是配置的多页面时的基本情况-------------

此时对应到qiankun中,假设我们的 activeRule: ‘/wdcsFrame’,那么qiankun中的url地址为http://192.18.XX.XX:8080/wdcsFrame#/home才能成功激活并匹配路由,在这中情况下如果主服务是单页面,那么如果你想做其他的路由切换跳转,通过 ‘$router.push’ 修改路由,路由地址则是 http://192.168.XX.XX:8080/wdcsFrame#/XXXX,而由于主服务是单页面,默认的pathname是空 即 /#/,此时页面无法正常匹配路由。
解决方案:先使用$router.push跳转路由后,立即使用
window.location.pathname = “”将其清空,主服务才能匹配路由;另外如果切换时,子服务没有被销毁(虽然我也没弄清楚咋销毁,路由切换时子服务的unmont也没进),且子服务存在路径 ‘/’ 的重定向设置。如果先清空pathname,路由会被重定向,而后面的$router.push不会运行(不知道为什么,debgger也没进去),此时路由同样无法匹配激活