# 微前端框架vite **Repository Path**: fang-yanwen/micro-front-end-framework-vite ## Basic Information - **Project Name**: 微前端框架vite - **Description**: 技术栈:vite+vue3+ts+qiankun - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2024-01-08 - **Last Updated**: 2025-02-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 微前端框架 #### 介绍 主应用技术框架:vite+qiankun+vue3+ts 子应用技术框架:vue3+vite+ts #### 安装教程 1. 使用pnpm i 下载主应用依赖 2. cd进入sonApp子应用文件夹将两个子应用使用pnpm i 下载依赖并使用pnpm dev启动子应用 3. 回到主应用使用 pnpm dev启动主应用,打开网页就可以了 # 子应用需要抛出qiankun需要的生命周期函数,否则会加载子应用失败,vue2、vue3+vite 、react各有不同 #### 教程博客:[微前端(qiankun)使用手册 - 简书 (jianshu.com)](https://www.jianshu.com/p/be4183f6ae68) ### 1.1、Vue2 + Webpack 子应用 #### 动态配置资源路径 1. 在 `src` 目录新增 `public-path.js`: ```js if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } ``` 1. 在应用入口 `index.js` 顶部导入 `public-path.js`。 ```js import "./public-path"; ``` #### 公开生命周期函数 ```js import "./public-path"; import Vue from "vue"; import App from "./App.vue"; import router from "./router"; import store from "./store"; Vue.config.productionTip = false; let app: Vue | null; function renderApp(container: string | HTMLElement) { app = new Vue({ router, store, render: (h) => h(App), }).$mount(container); } if (!window.__POWERED_BY_QIANKUN__) { renderApp("#app"); } export async function bootstrap() { console.log("vue2 app bootstrap"); } export async function mount(props: any) { renderApp(props.container.querySelector("#app")); } export async function unmount() { if (!app) { return; } app.$destroy(); app.$el.innerHTML = ""; app = null; } ``` #### 设置路由前缀 ```js const router = new VueRouter({ mode: "history", base: window.__POWERED_BY_QIANKUN__ ? "/vue2-app/" : process.env.BASE_URL, routes, }); ``` #### 修改打包配置(`vue.config.js`) ```js const { defineConfig } = require("@vue/cli-service"); const { name } = require("./package.json"); module.exports = defineConfig({ ..., devServer: { headers: { "Access-Control-Allow-Origin": "*", }, }, configureWebpack: { output: { library: `${name}-[name]`, libraryTarget: "umd", jsonpFunction: `webpackJsonp_${name}`, }, }, }); ``` ### 1.2、Vue3 + Vite 子应用 `qiankun` 默认不支持 `Vite`,要加载 `Vite` 构建的子应用,需要借助 [vite-plugin-qiankun](https://links.jianshu.com/go?to=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fvite-plugin-qiankun) 插件。 #### 安装 `vite-plugin-qiankun` ```bash yarn add vite-plugin-qiankun pnpm add vite-plugin-qiankun npm i vite-plugin-qiankun ``` #### 公开生命周期函数 ```js import { createApp, type App as VueApp } from "vue"; import { renderWithQiankun, qiankunWindow, type QiankunProps, } from "vite-plugin-qiankun/dist/helper"; import App from "./App.vue"; import router from "./router"; import "./assets/main.css"; let app: VueApp; function renderApp(container: any) { app = createApp(App); app.use(router); app.mount(container); } if (qiankunWindow.__POWERED_BY_QIANKUN__) { renderWithQiankun({ bootstrap, mount, unmount, update }); } else { renderApp("#app"); } async function bootstrap() { console.log("vue3 app bootstrap"); } async function mount(props: QiankunProps) { renderApp(props.container?.querySelector("#app")); } async function unmount() { app?.unmount(); } async function update() { console.log("vue3 app update"); } ``` #### 设置路由前缀,可以不管 ```js import { qiankunWindow } from "vite-plugin-qiankun/dist/helper"; import { name } from "../../package.json"; const base = qiankunWindow.__POWERED_BY_QIANKUN__ ? name : import.meta.env.BASE_URL; const router = createRouter({ history: createWebHistory(base), routes: routes, }); ``` #### 修改打包配置(`vite.config.ts`) ```js ... import qiankun from "vite-plugin-qiankun"; const port = 8382; const base = `http://localhost:${port}/`; export default defineConfig({ base: base, server: { port: port, origin: base, cors: true, }, plugins: [ ..., qiankun("vue3-app", { useDevMode: true, }), ], ..., }); ``` ### 1.3、React 子应用 #### 动态配置资源路径 1. 在 `src` 目录新增 `public-path.js`: ```js if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } ``` 1. 在应用入口 `index.js` 顶部导入 `public-path.js`。 ```js import "./public-path"; ``` #### 公开生命周期函数 ```js import "./public-path"; import React from "react"; import ReactDOM from "react-dom"; import { BrowserRouter } from "react-router-dom"; import App from "./App"; function renderRoot(container: HTMLElement) { ReactDOM.render( , container ); } if (!window.__POWERED_BY_QIANKUN__) { renderRoot(document.querySelector("#root")!); } export async function bootstrap() { console.log("react app bootstrap"); } export async function mount(props: any) { renderRoot(props.container?.querySelector("#root")); } export async function unmount(props: any) { ReactDOM.unmountComponentAtNode(props.container?.querySelector("#root")); } ``` #### 设置路由前缀 ```js ``` #### 修改 webpack 配置 1. 安装插件 `@rescripts/cli`,当然也可以选择其他的插件,例如 `react-app-rewired`。 ```bash yarn add -D @rescripts/cli pnpm add -D @rescripts/cli npm i -D @rescripts/cli ``` 1. 应用根目录新增 `.rescriptsrc.js` ```js const { name } = require("./package"); module.exports = { webpack: (config) => { config.output.library = `${name}-[name]`; config.output.libraryTarget = "umd"; config.output.jsonpFunction = `webpackJsonp_${name}`; config.output.globalObject = "window"; return config; }, devServer: (_) => { const config = _; config.headers = { "Access-Control-Allow-Origin": "*", }; config.historyApiFallback = true; config.hot = false; config.watchContentBase = false; config.liveReload = false; return config; }, }; ``` 1. 修改 `package.json` ```json - "start": "react-scripts start", + "start": "rescripts start", - "build": "react-scripts build", + "build": "rescripts build", ``` # 应用间通信 ### 1、props 传参 注册子应用时可以通过 props 传递参数给子应用。 ```js registerMicroApps([ { name: "Vue2App", entry: "//localhost:8381/", container: "#micro-app-container", activeRule: "/vue2-app", props: { nickname: "StoneHui", age: 30 } }, ]); ``` 子应用挂载时可通过 props 获取主应用传递的参数。 ```js export async function mount(props) { console.log(props.nickname, props.age); // StoneHui 30 } ``` ### 2、[initGlobalState(state)](https://links.jianshu.com/go?to=https%3A%2F%2Fqiankun.umijs.org%2Fzh%2Fapi%23initglobalstatestate) #### 定义全局状态,并返回通信方法,建议在主应用使用,微应用通过 props 获取通信方法。 **相关类型定义:** ```js /** * 定义全局状态,并返回通信方法 * * @param state: 全局状态 * @retuns 通信方法实例 */ function initGlobalState(state: Record): MicroAppStateActions /** * 通信方法 */ type MicroAppStateActions { /** * 在当前应用监听全局状态,有变更触发 callback * @param callback 状态变更回调函数 * @param fireImmediately,是否立即触发 callback 函数 */ onGlobalStateChange: (callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) => void /** * 按一级属性设置全局状态,微应用中只能修改已存在的一级属性 * @param callback 变更的全局状态 * @retuns 修改结果 */ setGlobalState: (state: Record) => boolean /** * 移除当前应用的状态监听,微应用 umount 时会默认调用 * @retuns 移除结果 */ offGlobalStateChange: () => boolean } /** * 全局状态变更的回调函数 * @param state 变更后的全局状态 * @param prevState 变更前的全局状态 */ type OnGlobalStateChangeCallback = (state: Record, prevState: Record) => void; ``` **使用示例:** 1. 主应用 ```js import { initGlobalState, MicroAppStateActions } from 'qiankun'; const state = { ... } // 初始化全局状态并获取操作函数 const actions: MicroAppStateActions = initGlobalState(state); // 监听全局状态变更 actions.onGlobalStateChange((state, prev) => { console.log(state, prev); }); // 更新全局状态 actions.setGlobalState(state); ``` 1. 子应用 ```js export function mount(props) { // 监听全局状态变更 props.onGlobalStateChange((state, prev) => { console.log(state, prev); }); // 更新全局状态 props.setGlobalState(state); } ``` ## 3、常见问题 ### TypeScript cannot find name `__webpack_public_path__`。 在 `src` 目录新增 `global.d.ts` 文件: ```js declare let __webpack_public_path__: string; interface Window { __POWERED_BY_QIANKUN__: boolean; __INJECTED_PUBLIC_PATH_BY_QIANKUN__: string; } ``` ### `__webpack_public_path__` 无效,静态资源路径错误。 将 `public-path.js` 的导入语句放在应用入口文件的第一行。 ### configuration.output has an unknown property 'jsonpFunction'. 将 `output.jsonpFunction` 更名为 `output.chunkLoadingGlobal`。 ```js // jsonpFunction: `webpackJsonp_${name}`, chunkLoadingGlobal: `webpackJsonp_${name}`, ``` ### 子应用内部路由跳转后无法切换到主应用或其他子应用且路由栈异常。 - `Vue` 通过路由守卫更新 `state` ```js router.afterEach(() => { // Vue2 Object.assign(history.state, { current: location.pathname }); // Vue3 Object.assign(history.state, { current: history.state.current === "/" ? "/" : location.pathname, }); }); ``` - `React` 通过 `popstate` 监听器更新 `state` ```js window.addEventListener("popstate", () => { Object.assign(history.state, { current: location.pathname }); }); ```