优雅的在Vite3+Vue3中使用乾坤框架搭建前端微服务

1 简介

微服务越来越流行,前面我已经写过一篇使用VueGenesis从零开始前端微服务开发的前端微服务解决方案的示例,但是该框架限定了我们只能使用Vue2一种语言(后续有没有更新我就不知道了,因为看到乾坤这个框架,我就没关注了)。

传送==>使用VueGenesis从零开始前端微服务开发

源代码地址
https://gitee.com/bitem/vite-vue3-qiankun

2 准备工作

使用vite工具创建好3个项目,并做好基本的配置。这方面可参考这里:使用Vite+Vue3搭建前端SSR应用(一)新建项目并配置基础环境。这里是我写搭建SSR项目的准备工作,在这里同样适用。

结构

里面使用了workspaces对多项目进行统一管理
其中:
ms-main 主应用基座
ms-one 子应用1
ms-two 子应用2

官方的例子中我觉得有点限制了我们的发挥,于是我就发挥想象力,改变了乾坤框架的使用方式,于是就有了这篇文章分享出来。

3 主应用

3.1 核心工作代码

将乾坤框架的思想与Vue动态路由结合,我们可以使用动态路由进行子应用的装载,而不是参照官方实例,使用虚假路由来动态创建dom节点后装载子应用,这样会造成vue报出一个无法匹配到路由的警告(看到警告就想给他弄掉)。

新建路由页面和路由配置

// src/route.ts
import { createRouter, createWebHistory } from 'vue-router'
// 这里借助的是vite约定式路由插件
import routes from '~pages'

export default function () {
  routes.forEach((item) => {
    // 增加二级路由
    // 这里是自定义动态路由用来转载子应用的容器路由页面,对应ms.views文件夹中的vue文件,可以多个子应用共用一个,也可以独立使用,更加灵活。
    // 同时,一级路由也可以转载子应用,相信看到这里,已经不难想象该如何编码了。
    if (item.name == "index") {
      item.children?.push({
        path: "/one/:path",
        name: "ms-one",
        component: () => import("./ms.views/one.vue")
      })
      item.children?.push({
        path: "/two/:path",
        name: "ms-two",
        component: () => import("./ms.views/two.vue")
      })
    }
  })

  console.log("router =>", routes)

  return createRouter({
    history: createWebHistory(),
    routes,
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

还有更简单的方式,借助约定式路由的动态路由方法,发挥想象吧,这个很简单了,就不在描述了。

route

核心代码

<script lang="ts">
// src/ms.views/one.vue
// two.vue类似,这个里面只写了一个子应用
import { defineComponent } from 'vue'
import { registerMicroApps, runAfterFirstMounted, start } from "qiankun/es"

export default defineComponent({

  mounted() {
    this.initQiankun()
  },

  methods: {
    initQiankun() {
      registerMicroApps(this.app, {})
      runAfterFirstMounted(() => {
        
      })
      start({ prefetch: true })
    }
  },
  setup() {
    const app = [{
      // 这个子应用是vue-cli创建的服务(vue3+ts,没有使用vite,使用的是webpack),用来接入测试用的,同时也是测试乾坤框架各应用之间技术架构不相关
      // 同时也测试一个容器可以装在多个子应用
      name: 'emesui-finance',
      entry: '/emesui-finance/',
      container: '#one-container',
      activeRule: '/fina'
    },{
      name: 'ms-one',
      entry: '/ms-one/',
      container: '#one-container',
      activeRule: '/one'
    }]

    return {
      app
    }
  }
})

</script>

<template>
  <div class="app-one-container">
    <div id="one-container">

    </div>
  </div>
</template>

<style scoped lang="less">
.app-one-container {

}
</style>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

防止div的id重复
我们将main.ts中的挂载id以及index.html中的默认div的id更改为 app_main

app.mount('#app_main')
1
<div id="app_main"></div>
1

配置子应用的开发代理

export default defineConfig({
  // 无关代码忽略
  server: {
    port: 3000,
    proxy: {
      "/ms-one": {
        target: "http://localhost:3001/",
        changeOrigin: true
      },
      "/ms-two": {
        target: "http://localhost:3002/",
        changeOrigin: true
      }
    }
  },
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

3.2 运行主应用

cd packages/ms-main
yarn dev
1
2

main-result

报错是因为你的子应用还没跑起来,别慌。

进行到这一步,如果你不能将主应用跑起来,看不到和我差不多的效果,你可以直接下载源码查看。源码地址在文章开头的简介中。

4 子应用

两个子应用都一样做,这里拿一个做实例,如果想看完整版,移步源代码,地址在文章开头的简介里。

子应用需要借助插件vite-plugin-qiankun

yarn add vite-plugin-qiankun
1

4.1 核心工作代码

核心配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import Pages from 'vite-plugin-pages'
import qiankun from "vite-plugin-qiankun"

export default defineConfig({
  plugins: [
    vue(),
    Pages({
      dirs: 'src/views'
    }),
    // 重点
    qiankun("ms-one", {
      useDevMode: true
    })
  ],
  // 重点
  base: "/ms-one/",

  server: {
    port: 3001
  },
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

核心代码route.ts

import { createRouter } from 'vue-router'
import routes from '~pages'
// history需要外面传入,因为要根据应用的运行环境决定基础路由
export default function (history:any) {
  return createRouter({
    history: history,
    routes,
  })
}
1
2
3
4
5
6
7
8
9

核心代码main.ts

import { createApp } from 'vue'
import '@/assets/style.css'
import App from './App.vue'
import { createWebHistory } from 'vue-router'
import createRouter from "./router"
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper'

// 定义三个变量分别为:路由、应用实例、路由History
let router: any = null
let instance: any = null
let history: any = null

function render(props: any | undefined) {
  console.log("__POWERED_BY_QIANKUN__", qiankunWindow.__POWERED_BY_QIANKUN__)
  const container = props ? props.container : undefined
  // 重点,前面的是页面路由,后面的是文件路由
  history = createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/one' : '/ms-one')
  router = createRouter(history)
  instance = createApp(App)
  instance.use(router)
  instance.mount(container ? container.querySelector('#app') : '#app')
}
// 重点,需要定义好生命周期函数
renderWithQiankun({
  mount(props: any) {
    render(props)
    instance.config.globalProperties.$onGlobalStateChange = props.onGlobalStateChange
    instance.config.globalProperties.$setGlobalState = props.setGlobalState
  },
  bootstrap() {
    console.log('%c%s', 'color: green;', 'vue3.0 app bootstraped')
  },
  unmount() {
    instance.unmount()
    instance._container.innerHTML = ''
    instance = null
    router = null
    history.destroy()
  },
  update() {
    console.log('update')
  }
})

// 独立启动的判断
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render(undefined)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

4.2 运行结果

独立运行
run.png

接入主应用框架运行
qiankun-run.png

5 后记

第二个子应用方法雷同,这里不再赘述,如果需要,可前往Gitee获取完整源码,源码地址在文章开始的简介里。