目录
前言
因为业务系统接入的需要,决定将一个vue3+vite+ts的主应用系统,改造成基于qiankun的微应用架构。此文记录了改造的过程及vue3微应用接入的种种问题。
网上有很多关于微应用改造的案例,但很多都没写部署之后什么情况。写了部署的,没有实操部署在二级目录、三级目录是什么情况,甚至没有对部署之后的情况做测试、没有说明。这是在整个改造过程中最难的一点,也是最困扰我的一个问题。我们所改造的应用说明:
- 主应用:vue3+vite+ts
- 微应用1:vue2,qiankun官网API是基于vue2+webpack,我们对vue2也进行了接入,但是在本篇文章中不做说明。
- 微应用2:vue3+vite,由于主应用已经是vue3的系统,所以,微应用也决定直接使用vue3框架
不懂的地方可以参考qiankun官方API,本文记录的是 基于vue3+vite的微应用,如何在vue3+vite的主应用中接入,以及完整的改造过程。首先有三个前提条件:
- 主应用为经典运营系统的左右布局结构(下图),要求将微应用接入为主应用的一个路由页面;
- 微应用和主应用部署在不同的服务;
- 主应用部署在二级目录。
图中所示的微应用菜单处,就是可以动态插拔微应用的菜单。
主应用
- 在主应用中注册微应用路由
const microRouters =[{path:'/vite-vue3-app2/',// path值必须与微应用中,路由前缀的`${parentBase}${packagejson.name}`中的${packagejson.name}值相同,否则访问会出错,${parentBase}为主应用部署的目录路径,后面内容中会讲component: Layout,name:'vite-vue3-app2',meta:{title:'微应用测试2',icon:'dashboard'},children:[// 想要显示的微应用路由,都需要在主应用中注册{path:'hello',component:()=>import('@/views/Portal.vue'),// 所有路由都使用同一个vue组件hidden:false,name:'app2-hello',meta:{title:'app2-hello',icon:'dashboard'}},{path:'home',component:()=>import('@/views/Portal.vue'),hidden:false,name:'app2-home',meta:{title:'app2-home',icon:'dashboard'}},{path:'about',component:()=>import('@/views/Portal.vue'),hidden:false,name:'app2-about',meta:{title:'app2-about',icon:'dashboard'}},]},]
- 注册微应用
registerMicroApps([{name:'app2',entry:'/app2/',// entry,访问时,主应用的路径中会带有/app2/,通过niginx代理进入微应用container:'#view-main',activeRule:location=>{return location.pathname.includes('/vite-vue3-app2')// 路由中包含/vite-vue3-app时,激活该微应用},props:{agg:'/vite-vue3-app2',// 注意:_parent_base是主应用的路由前缀(可以使用变量),在微应用启动时会使用到(!!!重点)_parent_base:'/portal/admin-console/'}}
注册微应用阶段,需要注意的事项:
- entry使用一个字符串
'/app2/'
,在服务配置中通过nginx代理转发到微应用;
- entry使用一个字符串
- 注册微应用时,传递参数
_parent_base
,这是主应用部署的二级目录,在微应用不独立运行时,路由需要携带该参数一起。
- 注册微应用时,传递参数
- 父级路由的
path: '/vite-vue3-app2/'
,必须与微应用中的路由前缀${parentBase}${packagejson.name}
中${packagejson.name}
的值相同,否则访问会出错。(${parentBase}
是主应用部署的目录路径,后面内容中会讲)
- 父级路由的
- 注册
Portal.vue
组件,并在组件mounted
中调用start
函数
<template><div id="view-main"class="sub-app-container"></div></template><script lang="ts" setup>import{ onMounted }from'vue';import{ start }from'qiankun'onMounted(()=>{if(!(window as any).qiankunStarted){(window as any).qiankunStarted =truestart({sandbox:{experimentalStyleIsolation:true,singular:false}});}})</script>
微应用
因为qiankun目前还没有支持vue3使用,官方API中的方法行不通,可以使用有大佬开发的
vite-plugin-qiankun
插件。首先在微应用中安装插件:
vite-plugin-qiankun
,然后按照如下方法修改:
1.**
main.js
文件修改**
import{ createApp }from'vue'// vue3引入import Cookies from'js-cookie'import ElementPlus from'element-plus'// 引入element-plusimport locale from'element-plus/lib/locale/lang/zh-cn'// 中文语言import'@/assets/styles/index.scss'// global cssimport App from'./App'import store from'./store'//store/**
* 这里需要特别注意:我们将router定义为了一个函数。
* 因为主应用部署在二级目录,当微应用不独立运行时,如果微应用的router没有带上主应用二级目录作为前缀,访问会出错,具体原因下面介绍
*/import{ router }from'./router'// routerimport plugins from'./plugins'// pluginsimport{ renderWithQiankun, qiankunWindow }from'vite-plugin-qiankun/dist/helper'let app =null// 独立运行时if(!qiankunWindow.__POWERED_BY_QIANKUN__){
app =createApp(App)
app.use(router('')).use(store).use(plugins)// router(''),独立运行,路由前缀为空
app.use(ElementPlus,{locale: locale,size: Cookies.get('size')||'default'})
app.mount('#app')}else{// 作为微应用运行renderWithQiankun({// 调用renderWithQiankunmount(props){
app =createApp(App)
app
.use(router(props._parent_base))// 路由前缀添加router(props._parent_base),_parent_base是从主应用中传过来的.use(store).use(plugins)
app.use(ElementPlus,{locale: locale,size: Cookies.get('size')||'default'})
app.mount(props.container ? props.container.querySelector('#app'):'#app')},bootstrap(){
console.log('-- bootstrap --')},update(){
console.log('-- update --')},unmount(){
console.log('-- unmount --', app)
app?.unmount()}})}
当微应用不独立运行时,为什么路由前缀中要加上主应用的部署目录?
- 首先,主应用不是部署在服务器的根目录,而是在
/portal/admin-console/
目录下。所以,访问该应用的任何一个页面,都需要加上该目录作为访问地址,如系统的home页,就需要这样访问:http://localhost:18080/portal/admin-console/index
,index是路由,前面是服务域名+目录。 - 其次,假设微应用的路由不加如上所讲的目录作为前缀,会怎么样呢?这是当时部署后遇到的一个最大的难题。
如上图所示,我们访问了
vite-vue3-app2/hello
这个路由,而在点击后却跳转到了
http://localhost:8082/vite-vue3-app2/portal/admin-console/vite-vue3-app2/hello
,页面404错误。对比之下就会发现,qiankun在调起微应用时,将微应用的路由前缀添加在了主应用的域名后面,我们之所以在主应用中添加与微应用前缀一致的路由名称,也是因为这个原因。
当访问以上页面时,访问的域名是
http://localhost:8082
,所以原本微应用中的路由前缀就会添加在8082后面,而不是添加在
/portal/admin-console/
后面。
而当我们按照如上所讲的,将微应用中的路由前缀改为
${parentBase}${packagejson.name}
时(请参考如下2.微应用路由定义),访问就会变的正常。如果主应用部署在根目录,则parenBase给空值。
这一点对于部署在非根目录的服务非常重要,但在官方API中没有说明。是在开发环境调试正常,部署之后遇到的巨坑。
- 微应用路由定义:
router.js
文件
import{ createWebHistory, createRouter }from'vue-router'import{ qiankunWindow }from'vite-plugin-qiankun/dist/helper'exportconst constantRoutes =[{path:'/home',component:()=>import('@/views/micro/home.vue'),hidden:true},{path:'/hello',component:()=>import('@/views/micro/hello.vue'),hidden:true},{path:'/about',component:()=>import('@/views/micro/about.vue'),hidden:true},];constrouter=(parentBase)=>{/**
* 区别作为微应用运行和独立运行时的路由base
* 1. 当作为微应用运行时:路由前缀为 ${parentBase}${packagejson.name}`
* - parentBase是从主应用中传过来的参数
* - packagejson.name是在package.json文件中定义的固定变量,是为了方便使用和便于区分,这个应该大家都能够理解
* 2. 独立运行时:路由前缀为/app2
*/const base = qiankunWindow.__POWERED_BY_QIANKUN__ ?`${parentBase}${packagejson.name}`:'/app2'returncreateRouter({history:createWebHistory(base),routes: constantRoutes,scrollBehavior(to, from, savedPosition){if(savedPosition){return savedPosition
}else{return{top:0}}},});}export{ router }
vite.config.js
文件修改
import{ defineConfig, loadEnv }from'vite'import path from'path'import createVitePlugins from'./vite/plugins'exportdefaultdefineConfig(({ mode, command })=>{const env =loadEnv(mode, process.cwd())const{VITE_APP_ENV,VITE_WEB_APP_DIR}= env
return{configureWebpack:{// webpack 配置devtool:'source-map',},/**
* 代理的问题:微应用打包的 base 必须跟主应用中的代理地址 entry 值一致
* 但是,加上 /app2/ 之后,就必须部署在 app2 目录,否则无法独立访问。
*/base:'/app2/',build:{assetsDir:"assets",},plugins:createVitePlugins(env, command ==='build'),resolve:{alias:{'~': path.resolve(__dirname,'./'),'@': path.resolve(__dirname,'./src')},extensions:['.mjs','.js','.ts','.jsx','.tsx','.json','.vue']},server:{...},// server配置省略}})
vite.config.js
中的配置,主要是修改
base
值,如代码片段中的注释:
- 微应用打包的
base
必须跟主应用中的代理地址 entry 值一致。 - 微应用必须部署在
base
目录,否则无法独立访问。
package.json
文件修改package.json
文件中主要是修改了name值,这是为了方便在router中使用,全局使用统一的变量值更便于理解。你也可以自己选择修改或不修改,这里的值改动,不会影响整体功能的运行
部署
- 主应用部署
主应用部署在
\portal\admin-console
目录下,如下图所示nginx服务目录结构:
nginx配置:
server {
listen 18080;
server_name localhost;......其它内容省略
location /portal/admin-console/{
root app;
index index.html;// 主应用是history模式,解决404问题
try_files $uri $uri//portal/admin-console/index.html;}// 后端服务的代理
location /dev-api/{
proxy_pass https://xxxxxxxxxxx/;}// 微应用代理
location /app2/{
proxy_pass http://localhost:18089/app2/;// 微应用部署的服务}
error_page 500502503504/50x.html;
location =/50x.html {
root html;}}
- 微应用部署 如前所示,微应用需要部署在
/app2
目录下,微应用的静态文件路径: nginx配置:
server {
listen 18089;
server_name localhost;// ......其它内容省略
location /app2/{
root app;
index index.html;// 404
try_files $uri $uri//app2/index.html;}
error_page 500502503504/50x.html;
location =/50x.html {
root html;}}
至此,qiankun微应用接入成功。主应用、微应用单独访问或在主应用中访问微应用,都能够正常访问。
在改造的过程中,遇到了两个难题:
entry
微应用的入口。官方只给出了很多个方案,但都没有我想要的:在菜单中添加微应用,还要通过代理来激活。这个问题的解决都还相对容易,比较难搞的是下面一个问题。- 主应用部署在了非根目录下,出现如上所述访问跳转错误的问题。这个问题的解决只能通过不断观察,观察每次路由访问时出现错误的情况,一步一步逐层破解,直到最后发现解决问题的办法。实际上,问题的最终解决看似很简单,但是破解问题的过程并不容易。参考官方API将每一步都测试没有问题了,但我的问题就是存在,翻遍搜索引擎也没有找到我想要的答案,只能自己一步一步破解。解决问题的过程很难熬,但解决之后的成就感也是满满的!
所以,遇到问题 还是可以多探索探索的呀!!!
版权归原作者 哟哟- 所有, 如有侵权,请联系我们删除。