前言
因為業(yè)務(wù)系統(tǒng)接入的需要,決定將一個vue3+vite+ts的主應(yīng)用系統(tǒng),改造成基于qiankun的微應(yīng)用架構(gòu)。此文記錄了改造的過程及vue3微應(yīng)用接入的種種問題。
網(wǎng)上有很多關(guān)于微應(yīng)用改造的案例,但很多都沒寫部署之后什么情況。寫了部署的,沒有實操部署在二級目錄、三級目錄是什么情況,甚至沒有對部署之后的情況做測試、沒有說明。這是在整個改造過程中最難的一點,也是最困擾我的一個問題。我們所改造的應(yīng)用說明:
- 主應(yīng)用:vue3+vite+ts
- 微應(yīng)用1:vue2,qiankun官網(wǎng)API是基于vue2+webpack,我們對vue2也進行了接入,但是在本篇文章中不做說明。
- 微應(yīng)用2:vue3+vite,由于主應(yīng)用已經(jīng)是vue3的系統(tǒng),所以,微應(yīng)用也決定直接使用vue3框架
不懂的地方可以參考qiankun官方API,本文記錄的是 基于vue3+vite的微應(yīng)用,如何在vue3+vite的主應(yīng)用中接入,以及完整的改造過程。首先有三個前提條件:
- 主應(yīng)用為經(jīng)典運營系統(tǒng)的左右布局結(jié)構(gòu)(下圖),要求將微應(yīng)用接入為主應(yīng)用的一個路由頁面;
- 微應(yīng)用和主應(yīng)用部署在不同的服務(wù);
- 主應(yīng)用部署在二級目錄。
圖中所示的微應(yīng)用菜單處,就是可以動態(tài)插拔微應(yīng)用的菜單。
主應(yīng)用
- 在主應(yīng)用中注冊微應(yīng)用路由
const microRouters = [
{
path: '/vite-vue3-app2/', // path值必須與微應(yīng)用中,路由前綴的`${parentBase}${packagejson.name}`中的${packagejson.name}值相同,否則訪問會出錯,${parentBase}為主應(yīng)用部署的目錄路徑,后面內(nèi)容中會講
component: Layout,
name: 'vite-vue3-app2',
meta: { title: '微應(yīng)用測試2', icon: 'dashboard' },
children: [ // 想要顯示的微應(yīng)用路由,都需要在主應(yīng)用中注冊
{
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' }
},
]
},
]
- 注冊微應(yīng)用
registerMicroApps([
{
name: 'app2',
entry: '/app2/', // entry,訪問時,主應(yīng)用的路徑中會帶有/app2/,通過niginx代理進入微應(yīng)用
container: '#view-main',
activeRule: location => {
return location.pathname.includes('/vite-vue3-app2') // 路由中包含/vite-vue3-app時,激活該微應(yīng)用
},
props: {
agg: '/vite-vue3-app2',
// 注意:_parent_base是主應(yīng)用的路由前綴(可以使用變量),在微應(yīng)用啟動時會使用到(?。?!重點)
_parent_base: '/portal/admin-console/'
}
}
注冊微應(yīng)用階段,需要注意的事項:
-
- entry使用一個字符串
'/app2/'
,在服務(wù)配置中通過nginx代理轉(zhuǎn)發(fā)到微應(yīng)用;
- entry使用一個字符串
-
- 注冊微應(yīng)用時,傳遞參數(shù)
_parent_base
,這是主應(yīng)用部署的二級目錄,在微應(yīng)用不獨立運行時,路由需要攜帶該參數(shù)一起。
- 注冊微應(yīng)用時,傳遞參數(shù)
-
- 父級路由的
path: '/vite-vue3-app2/'
,必須與微應(yīng)用中的路由前綴${parentBase}${packagejson.name}
中${packagejson.name}
的值相同,否則訪問會出錯。(${parentBase}
是主應(yīng)用部署的目錄路徑,后面內(nèi)容中會講)
- 父級路由的
- 注冊
Portal.vue
組件,并在組件mounted
中調(diào)用start
函數(shù)
<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 = true
start({ sandbox: { experimentalStyleIsolation: true, singular: false } });
}
})
</script>
微應(yīng)用
因為qiankun目前還沒有支持vue3使用,官方API中的方法行不通,可以使用有大佬開發(fā)的vite-plugin-qiankun
插件。首先在微應(yīng)用中安裝插件:vite-plugin-qiankun
,然后按照如下方法修改:
1.main.js
文件修改
import { createApp } from 'vue' // vue3引入
import Cookies from 'js-cookie'
import ElementPlus from 'element-plus' // 引入element-plus
import locale from 'element-plus/lib/locale/lang/zh-cn' // 中文語言
import '@/assets/styles/index.scss' // global css
import App from './App'
import store from './store' //store
/**
* 這里需要特別注意:我們將router定義為了一個函數(shù)。
* 因為主應(yīng)用部署在二級目錄,當(dāng)微應(yīng)用不獨立運行時,如果微應(yīng)用的router沒有帶上主應(yīng)用二級目錄作為前綴,訪問會出錯,具體原因下面介紹
*/
import { router } from './router' // router
import plugins from './plugins' // plugins
import { 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 {
// 作為微應(yīng)用運行
renderWithQiankun({ // 調(diào)用renderWithQiankun
mount(props) {
app = createApp(App)
app
.use(router(props._parent_base))// 路由前綴添加router(props._parent_base),_parent_base是從主應(yīng)用中傳過來的
.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()
}
})
}
當(dāng)微應(yīng)用不獨立運行時,為什么路由前綴中要加上主應(yīng)用的部署目錄?
- 首先,主應(yīng)用不是部署在服務(wù)器的根目錄,而是在
/portal/admin-console/
目錄下。所以,訪問該應(yīng)用的任何一個頁面,都需要加上該目錄作為訪問地址,如系統(tǒng)的home頁,就需要這樣訪問:http://localhost:18080/portal/admin-console/index
,index是路由,前面是服務(wù)域名+目錄。 - 其次,假設(shè)微應(yīng)用的路由不加如上所講的目錄作為前綴,會怎么樣呢?這是當(dāng)時部署后遇到的一個最大的難題。
如上圖所示,我們訪問了vite-vue3-app2/hello
這個路由,而在點擊后卻跳轉(zhuǎn)到了http://localhost:8082/vite-vue3-app2/portal/admin-console/vite-vue3-app2/hello
,頁面404錯誤。對比之下就會發(fā)現(xiàn),qiankun在調(diào)起微應(yīng)用時,將微應(yīng)用的路由前綴添加在了主應(yīng)用的域名后面,我們之所以在主應(yīng)用中添加與微應(yīng)用前綴一致的路由名稱,也是因為這個原因。
當(dāng)訪問以上頁面時,訪問的域名是http://localhost:8082
,所以原本微應(yīng)用中的路由前綴就會添加在8082后面,而不是添加在/portal/admin-console/
后面。
而當(dāng)我們按照如上所講的,將微應(yīng)用中的路由前綴改為${parentBase}${packagejson.name}
時(請參考如下2.微應(yīng)用路由定義),訪問就會變的正常。如果主應(yīng)用部署在根目錄,則parenBase給空值。
這一點對于部署在非根目錄的服務(wù)非常重要,但在官方API中沒有說明。是在開發(fā)環(huán)境調(diào)試正常,部署之后遇到的巨坑。
- 微應(yīng)用路由定義:
router.js
文件
import { createWebHistory, createRouter } from 'vue-router'
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
export const 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
},
];
const router = (parentBase) => {
/**
* 區(qū)別作為微應(yīng)用運行和獨立運行時的路由base
* 1. 當(dāng)作為微應(yīng)用運行時:路由前綴為 ${parentBase}${packagejson.name}`
* - parentBase是從主應(yīng)用中傳過來的參數(shù)
* - packagejson.name是在package.json文件中定義的固定變量,是為了方便使用和便于區(qū)分,這個應(yīng)該大家都能夠理解
* 2. 獨立運行時:路由前綴為/app2
*/
const base = qiankunWindow.__POWERED_BY_QIANKUN__ ? `${parentBase}${packagejson.name}` : '/app2'
return createRouter({
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'
export default defineConfig(({ mode, command }) => {
const env = loadEnv(mode, process.cwd())
const { VITE_APP_ENV, VITE_WEB_APP_DIR } = env
return {
configureWebpack: { // webpack 配置
devtool: 'source-map',
},
/**
* 代理的問題:微應(yīng)用打包的 base 必須跟主應(yīng)用中的代理地址 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
值,如代碼片段中的注釋:
- 微應(yīng)用打包的
base
必須跟主應(yīng)用中的代理地址 entry 值一致。 - 微應(yīng)用必須部署在
base
目錄,否則無法獨立訪問。
-
package.json
文件修改package.json
文件中主要是修改了name值,這是為了方便在router中使用,全局使用統(tǒng)一的變量值更便于理解。你也可以自己選擇修改或不修改,這里的值改動,不會影響整體功能的運行
部署
- 主應(yīng)用部署
主應(yīng)用部署在\portal\admin-console
目錄下,如下圖所示nginx服務(wù)目錄結(jié)構(gòu):
nginx配置:
server {
listen 18080;
server_name localhost;
......其它內(nèi)容省略
location /portal/admin-console/ {
root app;
index index.html;
// 主應(yīng)用是history模式,解決404問題
try_files $uri $uri/ /portal/admin-console/index.html;
}
// 后端服務(wù)的代理
location /dev-api/ {
proxy_pass https://xxxxxxxxxxx/;
}
// 微應(yīng)用代理
location /app2/ {
proxy_pass http://localhost:18089/app2/; // 微應(yīng)用部署的服務(wù)
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
-
微應(yīng)用部署
如前所示,微應(yīng)用需要部署在/app2
目錄下,微應(yīng)用的靜態(tài)文件路徑:
nginx配置:
server {
listen 18089;
server_name localhost;
// ......其它內(nèi)容省略
location /app2/ {
root app;
index index.html;
// 404
try_files $uri $uri/ /app2/index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
至此,qiankun微應(yīng)用接入成功。主應(yīng)用、微應(yīng)用單獨訪問或在主應(yīng)用中訪問微應(yīng)用,都能夠正常訪問。
在改造的過程中,遇到了兩個難題:文章來源:http://www.zghlxwxcb.cn/news/detail-795487.html
-
entry
微應(yīng)用的入口。官方只給出了很多個方案,但都沒有我想要的:在菜單中添加微應(yīng)用,還要通過代理來激活。這個問題的解決都還相對容易,比較難搞的是下面一個問題。 - 主應(yīng)用部署在了非根目錄下,出現(xiàn)如上所述訪問跳轉(zhuǎn)錯誤的問題。這個問題的解決只能通過不斷觀察,觀察每次路由訪問時出現(xiàn)錯誤的情況,一步一步逐層破解,直到最后發(fā)現(xiàn)解決問題的辦法。實際上,問題的最終解決看似很簡單,但是破解問題的過程并不容易。參考官方API將每一步都測試沒有問題了,但我的問題就是存在,翻遍搜索引擎也沒有找到我想要的答案,只能自己一步一步破解。解決問題的過程很難熬,但解決之后的成就感也是滿滿的!
所以,遇到問題 還是可以多探索探索的呀!??!文章來源地址http://www.zghlxwxcb.cn/news/detail-795487.html
到了這里,關(guān)于07. vue3+vite+qiankun搭建微應(yīng)用前端框架,并接入vue3微應(yīng)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!