当前位置: 首页 > news >正文

qiankun 微前端接入实战

微前端这个内容在之前就做过分享,但是对于完整的项目实战没有写过,在公司刚好有后台,解决一下之前遗留的登录态和权限的问题。

主应用

1、安装依赖

pnpm i qiankun

2、main.ts

采用 registerMicroApps 来注册子应用

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import 'src/styles/index.scss'
import router from 'src/router/index'
import { registerMicroApps } from 'qiankun'const app = createApp(App)app.use(router)
app.mount('#app')registerMicroApps([{name: 'h5App',entry: '//dev.yvyiai.com:8090/yvyiai-digital-human-h5',  // 确保路径不以斜杠结尾container: '#sub-container',activeRule: '/yvyiai-digital-human-web/h5App'}
])

3、编写路由

这里需要添加子应用的路径匹配(不然子应用带路由的话,主应用会404)

const routes = [{path: '/h5App',component: () => import('src/views/Layout/index.vue'),children: [{path: '/:pathMatch(.*)*',name: 'h5App',component: () => import('src/views/subApp/index.vue'),meta: {title: 'h5App'}}]}
]

4、subApp/index.vue

需要添加一个子应用的渲染容器节点,需要和 registerMicroApps 注册的子应用的 container 节点一致

<template><div id="sub-container"></div>
</template><script setup lang="ts">
import { start } from 'qiankun'
import { onMounted } from 'vue'onMounted(() => {// 启动 qiankun,添加错误处理start({sandbox: {experimentalStyleIsolation: true},prefetch: false, // 禁用预加载避免冲突// 添加全局错误处理globalContext: window})
})
</script>

子应用

1、安装依赖

pnpm i vite-plugin-qiankun -D

2、配置vite.config.ts

  • 如果子应用是webpack的话,可以看qiankun官网,vite需要使用插件。

  • 子应用同时需要支持跨域

  • 配置打包为umd

import qiankun from 'vite-plugin-qiankun'export default defineConfig({// 参数1:子应用名plugin: [qiankun('h5App', { useDevMode: true })],server: {// 开发环境的hosthost: 'dev.yvyiai.com',cors: true,// 添加跨域头部headers: {'Access-Control-Allow-Origin': '*','Access-Control-Allow-Methods':'GET, POST, PUT, DELETE, PATCH, OPTIONS','Access-Control-Allow-Headers':'X-Requested-With, content-type, Authorization'}},build: {lib: {entry: './src/main.ts', // 入口文件name: 'h5App', // 子应用名称fileName: 'h5App', // 打包后的文件名formats: ['umd'] // 打包为 UMD 格式}}
})

3、改造main.ts

webpack的应用改造方法有点不同,比vite简单

import { createApp, type App as AppInstance } from "vue";
import router from './router'
import App from './App.vue'
import {renderWithQiankun,qiankunWindow
} from 'vite-plugin-qiankun/dist/helper'let app: AppInstance | null = null
function render(props: any = {}) {const { container } = propsapp = createApp(App)app.use(router)app.mount(container ? container.querySelector("#app") : "#app");
}if (!qiankunWindow.__POWERED_BY_QIANKUN__) {render()
}renderWithQiankun({mount(props) {render(props)},bootstrap() {},unmount(_props) {if (app) {app.unmount()if (app._container) {app._container.innerHTML = ''}app = null}},update() {}
})

4、路由改造

这个项目的主子应用都配置了base route的,子应用在qiankun环境下,需要把base route换成主应用的base+route path

  • 主应用base route:yvyiai-digital-human-web

  • 主应用对应子应用的出口路由:/h5App

// 子应用router/index.ts
const ROUTER_BASE = 'yvyiai-digital-human-h5'const router = createRouter({history: createWebHistory(!qiankunWindow.__POWERED_BY_QIANKUN__ ? ROUTER_BASE : 'yvyiai-digital-human-web/h5App'),routes
})

对应如下:

  • 子应用独立运行路径: http://dev.yvyiai.com:8090/yvyiai-digital-human-h5/xxx

  • 在主应用下运行路径: http://dev.yvyiai.com:8080/yvyiai-digital-human-web/h5App/xxx

子应用在主应用运行的路径就要以主应用的base为准了,不然会找不到资源的。

开发模式下接口问题

在开发模式下,我们的子应用通常会做proxy代理,但是这就导致我主应用是没有子应用的代理配置的,例如:

  • 子应用单独运行代理请求url: http://dev.yvyiai.com:8090/liveApi/api1

  • 子应用在主应用下运行的请求url: http://http://dev.yvyiai.com:8080/liveApi/api1

这里的 /liveApi 就是子应用在vite配置中做的代理,但是在主应用下看到是没有这个代理的,所以请求会报错。

解决办法:

  1. 在后端对接口处理跨域,不采用代理的方式

  2. 在主应用的vite中也配置同样的代理

但是方法2这种方法会存在一个缺点就是:我的子应用很多,代理也很多,我当前的子应用下面就有快10个代理接口,如果子应用也多,就会导致主应用的配置不好维护。

上面的解决方法只是基础原理

最终方法:

可以采用 CMS 进行后端配置,在运行前去加载代理的应用数据(也就是我的子应用需要提前去CMS系统上注册)。

这种方法对主应用去注册子应用也同样有用,可以通过远程数据配置来动态注册应用,这样基座代码不用每次注册都进行修改。

新建 remote-proxy-loader.js ,用于去加载不同子应用的proxy,去主应用的 vite.config.ts 去使用即可。

const fs = require('fs');
const path = require('path');
const axios = require('axios');// 本地缓存文件路径
const PROXY_CACHE_PATH = path.resolve(__dirname, './proxy-cache.json');/*** 从远程获取代理配置*/
async function fetchRemoteProxyConfig() {try {const response = await axios.get('https://your-config-server.com/proxy-config');return response.data; // 假设返回格式: { subApp1: { ... }, subApp2: { ... } }} catch (error) {console.error('获取远程代理配置失败,使用本地缓存', error);// 尝试读取本地缓存if (fs.existsSync(PROXY_CACHE_PATH)) {return JSON.parse(fs.readFileSync(PROXY_CACHE_PATH, 'utf-8'));}return {}; // 无配置时返回空对象}
}/*** 保存配置到本地缓存*/
function saveProxyCache(config) {fs.writeFileSync(PROXY_CACHE_PATH, JSON.stringify(config, null, 2));
}/*** 获取当前应用的代理配置*/
async function getCurrentAppProxy(appName) {const allConfig = await fetchRemoteProxyConfig();// 保存到本地缓存saveProxyCache(allConfig);// 返回当前应用的配置return allConfig[appName] || {};
}module.exports = { getCurrentAppProxy };

登录态的问题

项目中少不了的就是接口或页面的权限问题,这对项目的安全性也是非常重要的。

1、接口权限

公司的项目采用的是cookie作为登录态校验的,核心思路是利用 cookie 的跨域特性和 qiankun 的通信机制来做控制。

1.1 原理

cookie 具有域(domain)属性,若主应用和子应用配置在同一主域名下(如主应用 app.example.com ,子应用 sub1.example.com ),可通过设置 domain=.example.com 实现 cookie 共享

1.2 实现方案

同域的情况下直接共享:
  1. 登录接口设置 cookie 时指定主域名
// 登录接口响应头设置(后端)
Set-Cookie: token=xxx; domain=.example.com; path=/; HttpOnly; Secure
  1. 主应用登录后,子应用自动获取同域名下的 cookie
同域的情况下的登录态同步:

当主应用与子应用不在同一域时:

  1. 主应用登录后,通过 qiankun 的全局通信机制通知子应用
// 主应用登录成功后
import { initGlobalState } from 'qiankun';const globalState = initGlobalState({token: 'xxx', // 登录后获取的 tokenisLogin: true
});// 主应用监听子应用消息
globalState.onGlobalStateChange((state, prev) => {console.log('主应用监听到状态变化', state, prev);
});
  1. 子应用监听主应用的登录状态
// 子应用中
export function mount(props) {// 监听主应用传递的登录状态props.onGlobalStateChange((state, prev) => {if (state.isLogin) {// 子应用存储 token 到本地或内存localStorage.setItem('token', state.token);}}, true);
}

2、页面权限

页面权限可以在登录的时候,通过动态路由生成权限路由(还是靠CMS去拿)。但只限于主应用的路由,子应用的路由的话,只能通过主子应用通信+接口(接口返回403状态,可以直接返回login页面)解决权限

效果展示

由于没有做样式的特殊处理,导致子应用的样式污染到了主应用。

即使qiankun设置了样式隔离,vite还是会有影响,所以需要手动处理样式(webpack可以做到完美隔离)

在这里插入图片描述

http://www.xdnf.cn/news/1475857.html

相关文章:

  • 在线教育系统源码选型指南:功能、性能与扩展性的全面对比
  • import type在模块引入中的作用
  • 从“能说话”到“会做事”:AI工具如何重塑普通人的工作与生活?
  • 语义切片技术深度解析:重新定义RAG时代的文本处理范式
  • 分布式通信平台测试报告
  • 【Neovim】Vi、Vim、Neovim 与 LazyVim:发展史
  • 【开题答辩全过程】以 “爱心”家政管理系统为例,包含答辩的问题和答案
  • Linux/UNIX系统编程手册笔记:共享库、进程间通信、管道和FIFO、内存映射以及虚拟内存操作
  • 宝塔PostgreSQL安装pgvecto插件contrib包实现向量存储
  • 2025年渗透测试面试题总结-54(题目+回答)
  • rom定制系列------小米8“无人直播”虚拟摄像头 刷机固件 实现解析过程
  • `vector_ip_ops`(内积操作)和 `vector_cosine_ops`(余弦相似度操作)的不同
  • 详解 ELO 评分系统
  • [光学原理与应用-414]:设计 - 深紫外皮秒脉冲激光器 - 元件 - 柱面镜:光学系统中的一维(焦线)调控专家(传统透镜是0维的点)
  • 《用 asyncio 构建异步任务队列:Python 并发编程的实战与思考》
  • java分布式场景怎么实现一个高效的 读-写锁
  • 友猫社区APP源码与小程序端部署详解
  • Redis数据库基础
  • MySQL中有哪些锁
  • MathJax - LaTeX:WordPress 公式精准呈现方案
  • Android Studio 构建变体中的资源选择顺序详解
  • UDP-Server(2)词典功能
  • git在Linux中的使用
  • mac-intel操作系统go-stock项目(股票分析工具)安装与配置指南
  • v0.29.3 敏感词性能优化之繁简体转换 opencc4j 优化
  • 大语言模型提示词工程详尽实战指南
  • 记一次uniapp+nutui-uniapp搭建项目
  • 计算机网络:无线局域网加密与认证方式
  • LeetCode算法日记 - Day 33: 最长公共前缀、最长回文子串
  • Linux | i.MX6ULL Tftp 烧写和 Nfs 启动(第十九章)