优雅的在Vite3+Vue3中使用乾坤框架搭建前端微服务
1 简介
微服务越来越流行,前面我已经写过一篇使用VueGenesis从零开始前端微服务开发的前端微服务解决方案的示例,但是该框架限定了我们只能使用Vue2一种语言(后续有没有更新我就不知道了,因为看到乾坤这个框架,我就没关注了)。
源代码地址
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,
})
}
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
还有更简单的方式,借助约定式路由的动态路由方法,发挥想象吧,这个很简单了,就不在描述了。
核心代码
<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>
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')
<div id="app_main"></div>
配置子应用的开发代理
export default defineConfig({
// 无关代码忽略
server: {
port: 3000,
proxy: {
"/ms-one": {
target: "http://localhost:3001/",
changeOrigin: true
},
"/ms-two": {
target: "http://localhost:3002/",
changeOrigin: true
}
}
},
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3.2 运行主应用
cd packages/ms-main
yarn dev
2
报错是因为你的子应用还没跑起来,别慌。
进行到这一步,如果你不能将主应用跑起来,看不到和我差不多的效果,你可以直接下载源码查看。源码地址在文章开头的简介中。
4 子应用
两个子应用都一样做,这里拿一个做实例,如果想看完整版,移步源代码,地址在文章开头的简介里。
子应用需要借助插件vite-plugin-qiankun
yarn add vite-plugin-qiankun
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
},
})
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,
})
}
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)
}
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 运行结果
独立运行
接入主应用框架运行
5 后记
第二个子应用方法雷同,这里不再赘述,如果需要,可前往Gitee获取完整源码,源码地址在文章开始的简介里。