vue3中使用qiankun微服务

239 阅读3分钟

一、主应用

1.提供一个容器

<div id="sub"></div>

2.注册子服务

import { initGlobalState, registerMicroApps, setDefaultMountApp, start } from 'qiankun'

    registerMicroApps(finalApps.value, {
      beforeMount: (app) => {
        const appItem = finalApps.value.find((v) => {
          return v.name === app.props.name
        })

        app.props = appItem.props
        nProgress.start()
      },
      afterMount: () => {
        nProgress.done()
      }
    })
    
    start({
      prefetch: 'all'
    })
    if (apps.length > 0) {
       setDefaultMountApp(apps[0].activeRule)
    }
finalApps.value:

[
   {
    activeRule: "/hbHome"
    alwaysShow: false
    container: "#sub"
    entry: "/microBtHome/"
    id: 97
    name: "HomePage",
    props: {...}
   },
   {
    activeRule: "/hbUser"
    alwaysShow: false
    container: "#sub"
    entry: "/microUser/"
    id: 98
    name: "UserManagement",
    props: {...}
   }
]
registerMicroApps(apps, lifeCycles?)
apps:
    1.name 必选,微应用的名称,微应用之间必须确保唯一
    2.entry 必选,微应用的入口。
        -   配置为字符串时,表示微应用的访问地址,例如 `https://qiankun.umijs.org/guide/`。
        -   配置为对象时,`html` 的值是微应用的 html 内容字符串,而不是微应用的访问地址。微应用的 `publicPath` 将会被设置为 `/`
    3.container 必选,微应用的容器节点的选择器或者 Element 实例
        -   如`container: '#root'` 或 `container: document.querySelector('#root')`4.activeRule
        -   支持直接配置字符串或字符串数组,如 `activeRule: '/app1'` 或 `activeRule: ['/app1', '/app2']`,当配置为字符串时会直接跟 url 中的路径部分做前缀匹配,匹配成功表明当前应用会被激活。
        -   支持配置一个 active function 函数或一组 active function。函数会传入当前 location 作为参数,函数返回 true 时表明当前微应用会被激活。如 `location => location.pathname.startsWith('/app1')`5.loader(function) 可选,loading 状态发生变化时会调用的方法。
    6.props(`object`) 可选,主应用需要传递给微应用的数据
    
LifeCycles:
    -   beforeLoad - `Lifecycle | Array<Lifecycle>` - 可选
    -   beforeMount - `Lifecycle | Array<Lifecycle>` - 可选
    -   afterMount - `Lifecycle | Array<Lifecycle>` - 可选
    -   beforeUnmount - `Lifecycle | Array<Lifecycle>` - 可选
    -   afterUnmount - `Lifecycle | Array<Lifecycle>` - 可选

二、微应用

入口文件 main.js 修改,为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围。

1. main.js中导出相应的生命周期钩子

import { createApp } from 'vue'
import App from './App.vue'
import routes from './router'
import store from './store'
import { createRouter, createWebHistory } from 'vue-router

import generateRoutes from './utils/generateRouter'

let app = null
let router

if (window.__POWERED_BY_QIANKUN__) {
  // 动态设置 webpack publicPath,防止资源加载出错
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
} else {
  router = createRouter({
    history: createWebHistory('/'),
    routes
  })
  app = createApp(App)
  installElementPlus(app)
  installSvg(app)

  app
    .use(store)
    .use(router)
    .mount('#sub-app')
}
//解析从主模块传递的数据
function render (props) {
  let finalRoutes
  if (process.env.NODE_ENV === 'development') {
    finalRoutes = routes
  } else {
    finalRoutes = generateRoutes(props.resources, routes)
  }
  const find = finalRoutes.filter(item => !item.hidden)
  if (finalRoutes.length > 0) {
    finalRoutes = [
      {
        path: '/',
        hidden: true,
        redirect: find[0].path
      },
      ...finalRoutes
    ]
  }
  store.commit('router/SET_ROUTES', finalRoutes)
  router = createRouter({
    history: createWebHistory(props.baseUrl),
    routes: finalRoutes
  })
  app = createApp(App)
  
  app
    .use(store)
    .use(router)
    .mount('#sub-app')
}

export const bootstrap = async () => {
  console.log('微服务已挂载')
}

export const mount = async props => {
  store.commit('router/SET_APP_NAME', props.appName)
  store.commit('router/SET_BASE_URL', props.baseUrl)
  store.dispatch('user/setUserInfo', props.userInfo)
  store.commit('user/SET_ALL_RESOURCES', props.allResources)
  store.commit('user/SET_GALOBAL_STATE', props)
  store.commit('api/SET_BASE_API', 'props.baseApi')
  props.onGlobalStateChange(state => {
    store.dispatch('user/setUserInfo', state)
    store.commit('router/SET_HISTORYLIST', state.historyList)
    store.commit('collapse/SET_COLLAPSE', state.isCollapse)
  })
  render(props)
}

export const unmount = async () => {
  app.unmount()
  console.log('微服务已卸载')
  // app.unmount('#sub-app')
  app = null
}

generateRouter.js文件



function hasPermission(names, route) {
  if (route.hidden) return true
  if (route.alwaysShow) return true
  if (route.name) return names.includes(route.name)
}
function generateRoutes(names, routes) {
  const arr = []
  routes.forEach(item => {
    const map = { ...item }
    if (hasPermission(names, map)) {
      if (map.children) {
        map.children = generateRoutes(names, map.children)
      }
      arr.push(map)
    }
  })
  return arr
}

export default generateRoutes
finalRoutes:


[
    {
        "path": "/baseConfig",
        "name": "crmMarketing.baseConfig",
        "meta": {
            "name": "基础数据"
        }
    },
    {
        "path": "/salesVolume",
        "name": "crmMarketing.salesVolume",
        "meta": {
            "name": "年度目标额"
        }
    },
    {
        "path": "/attendance",
        "name": "crmMarketing.attendance",
        "meta": {
            "name": "考勤管理"
        }
    },
    {
        "path": "/currentOnline",
        "name": "crmMarketing.currentOnline",
        "meta": {
            "name": "在线统计"
        }
    },
    {
        "path": "/trip",
        "name": "crmMarketing.trip",
        "meta": {
            "name": "行程管理"
        }
    },
    {
        "path": "/marketShare",
        "name": "crmMarketing.marketShare",
        "meta": {
            "name": "市场占有率"
        }
    },
    {
        "path": "/bannerConfig",
        "name": "crmMarketing.bannerConfig",
        "meta": {
            "name": "移动端导航"
        }
    },
    {
        "path": "/feedback",
        "name": "crmMarketing.feedback",
        "meta": {
            "name": "信息反馈"
        }
    },
    {
        "path": "/noticeConfig",
        "name": "crmMarketing.noticeConfig",
        "meta": {
            "name": "云盘通知"
        }
    },
    {
        "path": "/:pathMatch(.*)",
        "name": "404",
        "meta": {
            "name": "404"
        },
        "hidden": true
    }
]

2. 配置微应用的打包工具

webpack看官网: qiankun.umijs.org/zh/guide/ge…

vue.config.js中配置

const packageName = require('./package.json').name;
module.exports = {
    configureWebpack: {
        output: {
          library: `${packageName}-[name]`,
          libraryTarget: 'umd'
        }
     }
 }

三、主应用跳转不同子应用

1.主应用通过history.pushState方式

// url 为当前子应用的activeRule
function usePush(url) {
    history.pushState('', '', url)
}

2.子应用通过配置history来匹配主应用

//props.baseUrl 主应用传递过来的当前子应用的activeRule
let router
 router = createRouter({
    history: createWebHistory(props.baseUrl),
    routes: finalRoutes
  })

浏览器的 url 发生变化会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。