您现在的位置是:首页 >学无止境 >qiankun在主应用、子应用均为vue3+vite+typescript中的使用网站首页学无止境
qiankun在主应用、子应用均为vue3+vite+typescript中的使用
介绍
什么是微前端
微前端是一种架构风格,通过将一个单体应用程序拆分成多个小型独立应用程序来实现。每个小型应用程序都专注于一项特定功能,并且可以独立部署、扩展和维护。
为什么需要微前端
- 复杂性管理
大型单体应用程序随着时间的推移变得越来越复杂,难以维护
- 团队独立性
微前端允许团队根据其能力和需求独立工作,而不需要干扰其他团队
- 技术多样性
使用不同的技术堆栈开发各个独立应用程序,而无需考虑整体应用程序的技术选型
核心价值
- 技术栈无关
主框架不限制接入应用的技术栈,子应用具备完全自主权
- 独立开发、独立部署
子应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
- 独立运行时
独立运行时每个子应用之间状态隔离,运行时状态不共享
优点
- 可扩展性
通过添加新应用程序来扩展系统而无需修改原始代码
- 独立部署
每个应用程序都可以独立部署、减少了系统的停机时间
- 更好的可用性和容错性
由于应用程序之间相互独立,因此如果一个应用程序发生故障,其他应用程序可以继续运行
缺点
-
导致依赖项冗余,增加用户的流量负担
-
团队自洽程度的增加,可能会破坏协作
qiankun ——一套完整的微前端解决方案
qiankun能很方便的将一个巨石应用改造成一个基于微前端框架的系统,并且不再需要去关注各种过程中的技术细节,做到真正的开箱即用和生产可用
特点
- 简单
任意js框架均可使用,微应用接入像使用接入一个iframe系统一样简单,但实际不是iframe
- 完备
几乎包含所有构建微前端系统时所需的基本能力,如样式隔离、js沙箱、预加载等
- 生产可用
已在蚂蚁内外经受过足够大量的线上系统的考验及打磨,健壮性值得信赖
使用方法
主应用(vue3+vite+TypeScript)
-
创建项目
npm init vue@latest
-
安装qiankun
yarn add qiankun
-
main.ts
注册微应用import { registerMicroApps, addGlobalUncaughtErrorHandler, start } from 'qiankun'; // 注册子应用 registerMicroApps([ { // 子应用名称,name值必须与子应用vite.config.ts文件中plugins属性qiankun的第一个参数值一致 name: 'subApp', // 默认会加载这个路径下的html,解析里面的js entry: '//localhost:5174', // 加载的容器(微应用会显示到这个容器里面,一定要保证主应用中有这个容器) container: '#subAppContainerVue3', // 和app.vue配置的节点一致 // 匹配的路由 activeRule: '/junminronghe', // 访问:http://localhost:5174/juminronghe props: { mag: '我是主应用main', // 主应用向微应用传递参数 domain: 'http://localhost:5174', }, } // 再有其他子应用,同理 ]); // 启动 qiankun start({ prefetch:'all', // 预加载 sandbox: { experimentalStyleIsolation: true, // 开启沙箱模式,实验性方案 }, }); // 添加全局异常捕获 addGlobalUncaughtErrorHandler((handler) => { console.log("异常捕获", handler); });
-
app.vue
创建挂载节点,如再有其他子应用,也是同理<div id="subAppContainerVue3"></div>
-
访问子应用
<script setup lang="ts"> import { useRouter } from 'vue-router' const router = useRouter(); function goToFunc() { router.push('/junminronghe'); } </script> <template> <main> <div @click="goToFunc">子应用</div> </main> </template>
子应用(vue3+vite+TypeScript)
-
安装
vite-plugin-qiankun
(qiankun
官方不支持vite
)需要安装插件yarn add vite-plugin-qiankun
-
vite.config.ts
import { fileURLToPath, URL } from 'node:url' import vue from '@vitejs/plugin-vue' import qiankun from 'vite-plugin-qiankun' export default defineConfig({ plugins: [ vue(), // qiankun的第一个参数必须与主应用在main.ts中registerMicroApps的name值一致 qiankun('vue3', { useDevMode: true }) ], server:{ headers: { 'Access-Control-Allow-Origin': '*', // 主应用获取子应用时跨域响应头 }, }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } })
-
main.ts
import { createApp } from 'vue' import { renderWithQiankun, qiankunWindow, QiankunProps } from 'vite-plugin-qiankun/dist/helper' import App from './App.vue' import router from './router' const app = createApp(App) app.use(router) // app.mount('#app') // renderWithQiankun: 为子应用导出一些生命周期函数 供主应用在特殊的时机调用 // qiankunWindow: qiankunWindow.POWERED_BY_QIANKUN 可判断是否在qiankun环境下 const initQianKun = () => { renderWithQiankun({ // bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap // 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等 bootstrap() { console.log('bootstrap'); }, // 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法,也可以接受主应用传来的参数 mount(_props: any) { console.log('mount', _props); render(_props.container) }, // 应用每次 切出/卸载 会调用的unmount方法,通常在这里我们会卸载微应用的应用实例 unmount(_props: any) { console.log('unmount', _props); }, update: function (props: QiankunProps): void | Promise<void> { console.log('update'); } }); } const render = (container) => { // 如果是在主应用的环境下就挂载主应用的节点,否则挂载到本地 const appDom = container ? container : "#app" app.mount(appDom) } // 判断是否为乾坤环境,否则会报错[qiankun]: Target container with #subAppContainerVue3 not existed while subAppVue3 mounting! qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render(null)
-
router——》index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper' const routes: RouteRecordRaw[] = [ { path: '/', name: 'home', component: () => import('../views/home/index.vue') }, // 文章详情 { path: '/content', name: 'content', component: () => import('../views/content/index.vue') }, ]; const router = createRouter({ history: createWebHistory( // 在乾坤环境下时,将所有的路由地址前加上'/junminronghe',否则将跳转至主应用的相应页面(eg:跳转至这里的文章详情页面,如果不加'/junminronghe'的话,将跳转至主应用的'/content'页面,如果主应用没有这个页面,将跳转至404页面) qiankunWindow.__POWERED_BY_QIANKUN__ ? '/junminronghe' : '/' ), routes }) export default router
坑
1、子应用的本地资源(图片等)显示404
解决方法:
在子应用的vite.config.ts文件的server对象中配置origin
属性,配置为子应用的链接地址
server:{
port: 5174,
// origin:用于定义开发调试阶段生成资产的 origin
// 子应用的地址,用于解决主应用中子应用出现静态地址404问题,控制台报错显示该资源在主应用路劲下
origin: 'http://localhost:5174',
}
2、子应用的接口404
在创建axios
实例时,配置baseURL
子应用:
// 创建 axios 实例
const instance = axios.create({
// API 请求的默认前缀
// "/junminrongheApi"要跟主应用,vite.config.ts中,代理的地址一致,给所有的接口请求地址前加“/junminrongheApi"字符串,表明是junminronghe子应用的接口
baseURL: qiankunWindow.__POWERED_BY_QIANKUN__ ? '/junminrongheApi' : '',
timeout: 60 * 1000, //设置超时
headers: {
"Content-Type": "application/json;charset=UTF-8;",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
".AspNetCore.Culture": "c=zh-Hans|uic=zh-Hans",
},
});
主应用:vite.config.ts
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
// "/junminrongheApi"要跟子应用,service——》index.ts中,baseURL的地址一致(即axios的baseURL地址一致),
// 将junminronghe子应用的接口地址代理到其自己的地址(即target与主应用main.ts中registerMicroApps的entry值,保持一致)即可
"/junminrongheApi": {
target: 'http://localhost:5174', // 后台接口
changeOrigin: true,
secure: false, // 如果是https接口,需要配置这个参数
// ws: true, //websocket支持
rewrite: (path) => path.replace(/^/junminrongheApi/, ""),
},
}
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
3、子应用的线上图片地址显示404(一般不会有这个问题)
注:这个是因为这里的图片地址不是常规的地址,而是像这样的地址:sysFileInfo/preview?id=1666025519278534658,所以才会出现这个问题,这个问题是特例,正常的图片地址(eg:https://vitejs.cn/vite3-cn/logo.svg
)就不会有这个问题
- 普通的线上图片加载
解决方法:
在所有的图片地址前,手动拼接一个请求地址,可以封装一个图片组件,统一管理
eg:
<n-image :object-fit="props.objectFit" :src="previewUrl + props.url" />
- 富文本中的图片加载
富文本中的图片,在加载时,图片地址(sysFileInfo/preview?id=1666025519278534658)前会被拼上主应用的地址及前面配置的子应用路由拼接的'/junminronghe'
(目前不知道是为啥)
解决方法:
主应用vite.config.ts
export default defineConfig({
plugins: [vue()],
server: {
proxy: {
// "/junminrongheApi"要跟子应用,service——》index.ts中,baseURL的地址一致(即axios的baseURL地址一致),
// 将junminronghe子应用的接口地址代理到其自己的地址(即target与主应用main.ts中registerMicroApps的entry值,保持一致)即可
"/junminrongheApi": {
target: 'http://localhost:5174', // 后台接口
changeOrigin: true,
secure: false, // 如果是https接口,需要配置这个参数
// ws: true, //websocket支持
rewrite: (path) => path.replace(/^/junminrongheApi/, ""),
},
// 加这个代理是因为像子应用中如果有富文本,并且富文本中有图片的话,请求的图片会在主应用的地址后面拼上子应用的路由地址(即这里的junminronghe)后再是图片的地址,
// 当然是因为这里的子应用的图片地址不是常规的地址,而是像这样的地址:sysFileInfo/preview?id=1666025519278534658,才会有这样的问题
"/junminronghe": {
target: 'http://localhost:5174', // 后台接口
changeOrigin: true,
secure: false, // 如果是https接口,需要配置这个参数
// ws: true, //websocket支持
rewrite: (path) => path.replace(/^/junminronghe/, ""),
},
}
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})