需求:

A:主应用
B:子应用
项目框架:vue2 + 全家桶 + qiankun
应用间切换需要保存页面缓存(多页签缓存),通过vue keep-alive只能实现页面级缓存,在单独打开的应用里能实现缓存,但是子应用切换到主应用,那子应用的缓存失效
在这里插入图片描述

方案:

采用子应用切换时,将子应用作为一个vue实例,再次加载时使用保存的实例上的vnode替换vue实例化的时候的render函数

前提

1.项目已实现页面级缓存(其实就是通过keep-alive include来实现)

解决步骤

1.qiankun加载子应用的方式选择

1.通过registerMicroApps注册子应用,qiankun会通过自动加载匹配的子应用;
2.通过loadMicroApp手动注册子应用

第一种qiankun自动检测路有的变化来控制子应用的卸载和加载,我们无法监听到,也就无法去替换vnode,所以采用第二种,手动加载应用(通过路由的变化去监听下就行了)

//navbar。vau
watch: {
    $route(to, from) {
    //判断是否要加载 或卸载应用
      doQiankun(to, from)
      //这段代码时其他功能
      if (to.path.indexOf(ssoName) !== -1) {
        this.isShowssoAdmin = true
      } else {
        let timer = setTimeout(() => {
          this.isShowssoAdmin = false
          clearTimeout(timer)
        })
      }
    }
  },

//抽离的js

export const doQiankun = (to, form) => {
  if (to.path.indexOf(ssoName) !== -1 && (form.path.indexOf(ssoName) === -1 || !form.path)) {
    loadApp()
  } else if (to.path.indexOf(ssoName) !== -1 && form.path.indexOf(ssoName) !== -1) {
    console.log('子应用之间切换')
  } else {
    unLoadApp()
  }
}

export const loadApp = () => {
  // 手动加载微应用
  microApp = loadMicroApp({
    name: ssoName,
    entry: ssoUrl,
    container: `#${ssoName}`,
    props: { }
  })
}

/**
 * @description: 手动卸载微应用
 * @return {*}
 * @Date Changed:
 */
export const unLoadApp = () => {
  console.log('手动卸载微应用')
  // 手动卸载微应用
  if (microApp) microApp.unmount()
}

2.实现手动加载后,在子应用中unmount 卸载周期中存储实例

//main.js
//instance 其实就是new Vue()的实例
//loadedApplicationMap定义的对象
let loadedApplicationMap = {}
let instance = null
export async function unmount() {
  unmountCache(instance, loadedApplicationMap, instance.$router)
  router = null
}

/**
 * @description: qiankun卸载子应用时 保存子应用实例
 * @param {*} instance  子应用实例
 * @param {*} loadedApplicationMap 保存的对象
 * @param {*} _router 离开时的路由
 * @return {*}
 * @Date Changed:
 */
 //代码都不用改  保存一下退出应用前的实例
function unmountCache(instance = {}, loadedApplicationMap = {}, _router) {
  // 此处永远只会保存首次加载生成的实例
  const needCached = instance.cachedInstance || instance
  const cachedInstance = {}
  cachedInstance._vnode = needCached._vnode
  // keepalive设置为必须 防止进入时再次created,同keep-alive实现
  if (!cachedInstance._vnode.data.keepAlive) cachedInstance._vnode.data.keepAlive = true

  loadedApplicationMap._vnode = cachedInstance._vnode
  loadedApplicationMap.$router = _router
  loadedApplicationMap.apps = [].concat(_router.apps)
  loadedApplicationMap.currentRoute = { ..._router.currentRoute }


}

3.进入应用时,替换vnode
这里要区分是否是首次进入

光替换node 会导致原来的路由失效,所以同时也要针对路有进行处理

//main.js
let isFirst = true

function render(props = {}) {
  const { container } = props
  // 这里必须要new一个新的路由实例,否则无法响应URL的变化
  router = new VueRouter({
    // base: window.__POWERED_BY_QIANKUN__ ? '/ssoAdmin/' : '/',
    mode: 'hash',
    // mode: 'history',
    routes
  })

  // 这里主要是适配子应用的单独访问和继承访问
  let vnode = loadedApplicationMap._vnode || null
  if (isFirst) {
    // 首次进入应用
    routerBeforeEach() //自己定义的路由守卫
    instance = newVueInstance(vnode).$mount(container ? container.querySelector('#app') : '#app')
    isFirst = false
  } else {
    //  router使用缓存命中
    // router = loadedApplicationMap.$router;

    // 让当前路由在最初的Vue实例上可用
    router.apps.push(...loadedApplicationMap.apps)
    routerBeforeEach() //自己定义的路由守卫
    instance = newVueInstance(vnode)

    // 当前路由和上一次卸载时不一致,则切换至新路由
    const { path } = router.currentRoute
    const oldPath = loadedApplicationMap.currentRoute.path
    if (path !== oldPath) {
    //replace 替换 浏览器记录
      loadedApplicationMap.$router.replace({ path, query: router.currentRoute.query })
    }

    instance.$mount(container ? container.querySelector('#app') : '#app')
  }
}

Logo

前往低代码交流专区

更多推荐