📦 qiankun微前端接入实战

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

主应用

1、安装依赖

bash 复制代码
pnpm i qiankun

2、main.ts

采用 registerMicroApps 来注册子应用

ts 复制代码
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)

ts 复制代码
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 节点一致

html 复制代码
<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、安装依赖

bash 复制代码
pnpm i vite-plugin-qiankun -D

2、配置vite.config.ts

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

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

  • 配置打包为umd

ts 复制代码
import qiankun from 'vite-plugin-qiankun'

export default defineConfig({
  // 参数1:子应用名
  plugin: [qiankun('h5App', { useDevMode: true })],
  server: {
    // 开发环境的host
    host: '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简单

ts 复制代码
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 } = props
  app = 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

ts 复制代码
// 子应用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
})

对应如下:

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

开发模式下接口问题

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

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

解决办法:

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

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

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

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

最终方法:

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

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

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

js 复制代码
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 时指定主域名
bash 复制代码
// 登录接口响应头设置(后端)
Set-Cookie: token=xxx; domain=.example.com; path=/; HttpOnly; Secure
  1. 主应用登录后,子应用自动获取同域名下的 cookie
同域的情况下的登录态同步:

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

  1. 主应用登录后,通过 qiankun 的全局通信机制通知子应用
ts 复制代码
// 主应用登录成功后
import { initGlobalState } from 'qiankun';


const globalState = initGlobalState({
  token: 'xxx', // 登录后获取的 token
  isLogin: true
});


// 主应用监听子应用消息
globalState.onGlobalStateChange((state, prev) => {
  console.log('主应用监听到状态变化', state, prev);
});
  1. 子应用监听主应用的登录状态
js 复制代码
// 子应用中
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可以做到完美隔离)

相关推荐
小桥风满袖3 小时前
极简三分钟ES6 - Symbol
前端·javascript
子兮曰3 小时前
🚀Map的20个神操作,90%的开发者浪费了它的潜力!最后的致命缺陷让你少熬3天夜!
前端·javascript·ecmascript 6
NewChapter °3 小时前
如何通过 Gitee API 上传文件到指定仓库
前端·vue.js·gitee·uni-app
练习时长两年半的Java练习生(升级中)3 小时前
从0开始学习Java+AI知识点总结-30.前端web开发(JS+Vue+Ajax)
前端·javascript·vue.js·学习·web
vipbic3 小时前
关于Vue打包的遇到模板引擎解析的引号问题
前端·webpack
qq_510351593 小时前
vw 和 clamp()
前端·css·html
良木林3 小时前
JS中正则表达式的运用
前端·javascript·正则表达式
绝无仅有3 小时前
未来教育行业的 Go 服务开发解决方案与实践
后端·面试·github
芭拉拉小魔仙3 小时前
【Vue3+TypeScript】H5项目实现企业微信OAuth2.0授权登录完整指南
javascript·typescript·企业微信