前言

qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。

本文主要记录下如何接入 qiankun 微前端。主应用使用 vue2,子应用使用 vue3、react。

一、主应用

主应用不限技术栈,只需要提供一个容器 DOM,然后注册微应用并 start 即可。

1⃣️、创建项目

// @vue/cli 5.0.4
npm install @vue/cli -gvue create main-vue

主应用选择 vue2.x 版本。 具体创建步骤,便不在此一一叙述。

项目创建之后,配置路由,页面布局等。整体效果如下图。

2⃣️、安装 qiankun

npm i qiankun -S

3⃣️、 注册微应用并启动

新建微应用子列表文件 micros/app.js

// src/micros/app.js
// 子应用列表
const apps = [{name: 'vue2-app', // 子应用app name 推荐与子应用的package的name一致entry: '//localhost:8081/', // 子应用的入口地址,就是你子应用运行起来的地址container: '#micro-container', // 挂载子应用内容的dom节点 `# + dom id`【见上面app.vue】activeRule: '/vue2App' // 子应用的路由前缀},{name: 'vue3-app',entry: '//localhost:8082/',container: '#micro-container',activeRule: '/vue3App'},{name: 'react-app',entry: '//localhost:8083/',container: '#micro-container',activeRule: '/react'}
]export default apps

注册微应用

// src/micros/index.js
import { addGlobalUncaughtErrorHandler, registerMicroApps, start} from 'qiankun'
// 微应用的信息
import apps from './app'/*** 注册微应用* 第一个参数 - 微应用的注册信息* 第二个参数 - 全局生命周期钩子*/
registerMicroApps(apps, {// qiankun 生命周期钩子 - 微应用加载前beforeLoad: (app) => {// 加载微应用前,加载进度条console.log('before load=====', app.name)return Promise.resolve()},// qiankun 生命周期钩子 - 微应用挂载后afterMount: (app) => {// 加载微应用前,进度条加载完成console.log('after mount=====', app.name)return Promise.resolve()}
}
)/*** 添加全局的未捕获异常处理器*/
addGlobalUncaughtErrorHandler((event) => {console.error(event)const { message: msg } = eventif (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {console.error('微应用加载失败,请检查应用是否可运行')}
})// 导出 qiankun 的启动函数
export default start

配置主应用路由

// src/router/index.jsimport Vue from 'vue'
import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [{path: '/',name: 'home',component: () => import('@/components/Home.vue'),children: [{path: '/',name: 'hello',component: () => import('@/views/HomeView.vue')},{path: '/vue2App',name: 'vue2App'}, {path: '/vue3App',name: 'vue3App'}, {path: '/vue3App/list',name: 'vueAppList'}, {path: '/react',name: 'react'}]}
]const router = new VueRouter({mode: 'history',base: process.env.BASE_URL,routes
})export default router

页面设置子应用的挂载节点

<template><div class="wrapper"><MyHeader></MyHeader><el-container class="content"><el-aside width="200px"><MySider></MySider></el-aside><el-main><router-view></router-view><!-- 挂载子应用节点 --><div id="micro-container"></div></el-main></el-container></div>
</template><script>import MyHeader from './Header.vue'import MySider from './Sider.vue'export default {name: 'MyHome',components: {MyHeader,MySider},data() {return {}}}
</script><style lang="less">.content {height: calc(100% - 50px);}
</style>

在 main.js 中引入并启动 qiankun

// src/main.jsimport Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import '../src/assets/style/reset.less'
import './plugins/element.js'import start from '@/micros'
// 启动
start()Vue.config.productionTip = falsenew Vue({router,store,render: h => h(App)
}).$mount('#app')

二、微应用

微应用分为有 webpack 构建和无 webpack 构建项目,有 webpack 的微应用(主要是指 Vue、React、Angular)需要做的事情有:

  • 新增 public-path.js 文件,用于修改运行时的 publicPath
  • 微应用建议使用 history 模式的路由,需要设置路由 base,值和它的 activeRule 是一样的。
  • 在入口文件最顶部引入 public-path.js,修改并导出三个生命周期函数。
  • 修改 webpack 打包,允许开发环境跨域和 umd 打包。

无 webpack 构建的微应用直接将 lifecycles 挂载到 window 上即可。

微应用无需安装 qiankun。

三、vue2-app 微应用

1⃣️、创建项目

// vue-cli 2.9.6
npm install vue-cli -gnpm install webpack-cli -gnpm init webpack vue-app

2⃣️、在 src 目录新增 public-path.js

// src/public-path.js
if(window.__POWERED_BY_QIANKUN__) {__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

修改路由文件,建议使用history 模式的路由,并设置路由 base,值和它的 activeRule 是一样的。

// src/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'Vue.use(Router)export default new Router({mode: 'history',base: window.__POWERED_BY_QIANKUN__ ? "/vue2App" : "/",routes: [{path: '/',name: 'HelloWorld',component: HelloWorld}]
})

入口文件 main.js 修改,为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围。并导出三个生命周期函数。

// src/main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import "./public-path";Vue.config.productionTip = false// 定义一个Vue实例
let instance = null
// 渲染方法
function render(props = {}) {const { container } = propsinstance = new Vue({router,render: (h) => h(App)}).$mount(container ? container.querySelector('#app'): '#app')
}
// 独立运行时
if(!window.__POWERED_BY_QIANKUN__) {render()
}
//暴露主应用生命周期钩子
/*** bootstrap : 在微应用初始化的时候调用一次,之后的生命周期里不再调用*/
export async function bootstrap() {console.log('vue2-app bootstraped');
}
/*** mount : 在应用每次进入时调用*/
export async function mount(props) {console.log('vue2-app mount', props);render(props);
}
/*** unmount :应用每次 切出/卸载 均会调用*/
export async function unmount() {console.log("vue2-app unmount")instance.$destroy();instance.$el.innerHTML = '';instance = null;
}

修改 webpack 打包,允许开发环境跨域和 umd 打包。

// build/webpack.base.conf.js
'use strict'
const config = require('../config')
const APP_NAME = require('../package.json').namemodule.exports = {output: {path: config.build.assetsRoot,filename: '[name].js',publicPath: process.env.NODE_ENV === 'production'? config.build.assetsPublicPath: config.dev.assetsPublicPath,// 微应用的包名,这里与主应用中注册的微应用名称一致library: APP_NAME,// 将你的 library 暴露为所有的模块定义下都可运行的方式libraryTarget: "umd",// 按需加载相关,设置为 webpackJsonp_VueMicroApp 即可jsonpFunction: `webpackJsonp_${APP_NAME}`,},...
}
// build/webpack.dev.conf.js
const devWebpackConfig = merge(baseWebpackConfig, {...devServer: {...// 关闭主机检查,使微应用可以被 fetchdisableHostCheck: true,// 配置跨域请求头,解决开发环境的跨域问题headers: {"Access-Control-Allow-Origin": "*",}}
})

运行效果如下:

四、vue3-app 微应用

1⃣️、创建项目

// @vue/cli 5.0.4
npm install @vue/cli -gvue create vue3-app

2⃣️、在 src 目录新增 public-path.ts

// src/public-path.ts
if ((window as any).__POWERED_BY_QIANKUN__) {__webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

修改路由文件。

// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";const routes: Array<RouteRecordRaw> = [{path: "/",name: "home",component: () => import("@/components/Home.vue"),children: [{path: "/",name: "index",component: () => import("../views/HomeView.vue"),},{path: "/list",name: "list",component: () => import("../views/AboutView.vue"),},],},
];const router = createRouter({history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? "/vue3App" : process.env.BASE_URL),routes,
});export default router;

入口文件 main.ts 修改

// src/main.ts
import Vue, { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
import "./public-path.ts";// 定义一个Vue实例
let instance: Vue.App<Element>;
// 需要定义该接口,否则`/src/router/index.ts`无法使用`Window.__POWERED_BY_QIANKUN__`
declare global {interface Window {__POWERED_BY_QIANKUN__?: string;}
}
interface IRenderProps {container: Element | string;
}// 渲染方法
function render(props: IRenderProps) {const { container } = props;instance = createApp(App);instance.use(store).use(router).mount(container instanceof Element? (container.querySelector("#app") as Element): (container as string));
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {render({ container: "#app" });
}//暴露主应用生命周期钩子
/*** bootstrap : 在微应用初始化的时候调用一次,之后的生命周期里不再调用*/
export async function bootstrap() {console.log("vue3-app bootstraped");
}/*** mount : 在应用每次进入时调用*/
export async function mount(props: any) {console.log("mount vue3-app", props);render(props);
}/*** unmount :应用每次 切出/卸载 均会调用*/
export async function unmount() {console.log("unmount vue3-app app");instance.unmount();
}

修改 webpack 打包,允许开发环境跨域和 umd 打包。

注意:webpack5 中 jsonpFunction 修改为 chunkLoadingGlobal

// vue.config.js
const path = require("path");const APP_NAME = require("./package.json").name;function resolve(dir) {return path.join(__dirname, dir);
}module.exports = {outputDir: "dist",assetsDir: "static",filenameHashing: true,devServer: {host: "localhost",hot: true,port: 8082,client: {overlay: {errors: true,warnings: false,},},// 配置跨域请求头,解决开发环境的跨域问题headers: {"Access-Control-Allow-Origin": "*",},},// 自定义webpack配置configureWebpack: {resolve: {alias: {"@": resolve("src"),},},output: {// 把子应用打包成 umd 库格式// // 微应用的包名,这里与主应用中注册的微应用名称一致library: APP_NAME,// 将你的 library 暴露为所有的模块定义下都可运行的方式libraryTarget: "umd",// 按需加载相关,设置为 webpackJsonp_微应用名称 即可chunkLoadingGlobal: `webpackJsonp_${APP_NAME}`,},},
};

运行效果如下:

五、react-app 微应用

1⃣️、创建项目,以 create-react-app 生成的 react 17 项目为例,搭配 react-router-dom 6.x。

npx create-react-app react-app --template typescriptnpm i react-router-dom

在根目录下添加 .env 文件,设置项目监听的端口

// react-app/.env
PORT=8083
BROWSER=none

2⃣️、新建 public-path.ts

// src/public-path.ts
if ((window as any).__POWERED_BY_QIANKUN__) {__webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

如上面代码报错,可以通过补充定义进行修复。以下代码最好放到全局引入的 TypeScript 定义文件中。

interface Window {__POWERED_BY_QIANKUN__?: string__INJECTED_PUBLIC_PATH_BY_QIANKUN__?: string
}declare let __webpack_public_path__: string | undefined

设置 history 模式路由的 base

// src/App.tsx
import React from 'react';
import { Routes, Route, BrowserRouter} from 'react-router-dom'
import './App.css';
import Home from './components/home';function App() {return (<div>{/* 设置路由命名空间 */}<BrowserRouter basename={window.__POWERED_BY_QIANKUN__ ? '/react' : '/'}><Routes><Route path="/" element={<Home />} /></Routes></BrowserRouter></div>);
}export default App;

修改入口文件 index.tsx

// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import './types.d.ts'
import "./public-path";/*** bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。*/
export async function bootstrap() {console.log('react-app bootstraped');
}
/*** 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法*/
export async function mount(props: any) {console.log('react-app mount');ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/*** 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例*/
export async function unmount(props: any) {console.log('react-app unmount');ReactDOM.unmountComponentAtNode(props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}
/*** 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效*/
export async function update(props: any) {console.log('react-app update props', props);
}ReactDOM.render(<React.StrictMode><App /></React.StrictMode>,document.getElementById('root')
);// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

修改 webpack 配置,安装插件 react-app-rewired

npm install react-app-rewired -D

修改 package.json

// react-app/package.json
"scripts": {"start": "react-app-rewired start","build": "react-app-rewired build","test": "react-app-rewired test","eject": "react-app-rewired eject"
}

在 react-app-rewired 配置完成后,新建 config-overrides.js 文件来配置 webpack。

// react-app/config-overrides.js
const path = require("path");
const APP_NAME = require("./package.json").name;module.exports = {webpack: (config) => {// 微应用的包名,这里与主应用中注册的微应用名称一致config.output.library = APP_NAME;// 将你的 library 暴露为所有的模块定义下都可运行的方式config.output.libraryTarget = "umd";// 按需加载相关,设置为 webpackJsonp_VueMicroApp 即可config.output.chunkLoadingGlobal = `webpackJsonp_${APP_NAME}`;config.output.globalObject = 'window';config.output.publicPath = `//localhost:${process.env.PORT}/`;config.resolve.alias = {...config.resolve.alias,"@": path.resolve(__dirname, "src"),};return config;},devServer: function (configFunction) {return function (proxy, allowedHost) {const config = configFunction(proxy, allowedHost);// 关闭主机检查,使微应用可以被 fetch// config.disableHostCheck = true;config.allowedHosts = "all";// 配置跨域请求头,解决开发环境的跨域问题config.headers = {"Access-Control-Allow-Origin": "*",};// 配置 history 模式config.historyApiFallback = true;return config;};},
};

3⃣️、运行效果如下

微前端 - qiankun相关推荐

  1. qiankun 传统项目配置_微前端 qiankun 项目实践

    原标题:微前端 qiankun 项目实践 作者:zxh1307 https://juejin.im/post/5ea55417e51d4546e347fda9 导语 最近在做微前端的项目 , 过程中真 ...

  2. 基于微前端qiankun的多页签缓存方案实践

    作者:vivo 互联网前端团队- Tang Xiao 本文梳理了基于阿里开源微前端框架qiankun,实现多页签及子应用缓存的方案,同时还类比了多个不同方案之间的区别及优劣势,为使用微前端进行多页签开 ...

  3. 微前端qiankun 问题

    微前端qiankun 遇到问题,连接子应用之后报错 We're sorry but micro-front-web3 doesn't work properly without JavaScript ...

  4. py获取前端的参数_微前端 qiankun 项目实践

    (给前端大全加星标,提升前端技能) 作者:zxh1307 https://juejin.im/post/5ea55417e51d4546e347fda9 导语 最近在做微前端的项目 , 过程中真是踩了 ...

  5. 微前端qiankun从搭建到部署的实践

    最近负责的新项目用到了qiankun,写篇文章分享下实战中遇到的一些问题和思考. 示例代码: github.com/fengxianqi/-. 在线demo:qiankun.fengxianqi.co ...

  6. 微前端qiankun使用+踩坑

    背景 项目使用qiankun 改造的背景: 项目A.项目B.项目C: 项目A和项目B具有清晰的服务边界,从服务类型的角度能够分为两个项目. 在公司项目一体化的背景下,所有的项目又应该是一个项目. 项目 ...

  7. 微前端 - qiankun 应用间通信

    前言 上篇主要记录了如果使用 qiankun,本文记录下 qiankun 应用间如何通信. 一.如何划分子应用 在微前端架构中,我们应该按业务划分出对应的子应用,而不是通过功能模块划分子应用.主要原因 ...

  8. 微前端-qiankun

    概念 使用框架:qiankun 介绍: qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单.无痛的构建一个生产可用微前端架构系统. qiankun官网:Go 当使用 ...

  9. 微前端qiankun 子应用使用阿里字体图标不显示

    微前端打包部署后,子应用的iconfont的字体图标显示报错, 原因:打包后请求字体的路径是获取主应用下面的文件 解决方案:将字体图标打包成base64显示 // 打包成base64config.mo ...

最新文章

  1. 计算机科学与技术专业认证研讨,信息学院召开计算机科学与技术专业IEET认证暨人才培养方案修订研讨会...
  2. 透过现象看本质,图解支持向量机
  3. CC 攻击检测研究现状
  4. visual studio code Python终端运行_VS Code 6 月 Python 扩展更新
  5. notes belonging to given user
  6. 高斯白噪声下基于EM的多径时延估计
  7. Apollo进阶课程㉝丨Apollo ROS原理—2
  8. 两数之和(Leetcode第1题)
  9. 暴涨30%!马斯克突然杀入推特:当上最大股东!
  10. 【Elasticsearch】Elasticsearch 基于scoll技术滚动搜索大量数据
  11. Tree UVA - 548(二叉树递归遍历)
  12. 元素显示模式转换(HTML、CSS)
  13. 语音识别揭秘:你的手机究竟有多理解你?
  14. github 新建远程仓库 及 删除远程仓库
  15. 初中计算机授课教案模板,初中语文的教案模板(精选5篇)
  16. 面渣逆袭:Java并发六十问,快来看看你会多少道
  17. 怎样在Word添加页码?经验干货!word如何插入页码?
  18. 初体验之rawkv learner recover灾备切换
  19. python遇到错误跳过_python如何设置报错跳过?
  20. POJ 1625 Censored!

热门文章

  1. 知道Unicode表的数字,怎么用java推出相对应的日文?
  2. Mac OS无法进入系统/数据备份/重装系统方法步骤
  3. 打开PDF文件时,出现“打开本文档时发生错误。无法找到本文件。”解决办法,亲自尝试成功
  4. uniapp使用诺诺人脸认证(微信小程序)sdk
  5. 【定制开发】【M8】推荐一款强大的磁盘清理小工具,专治磁盘爆满(不到1M)
  6. 分子动力学模拟之SETTLE约束算法
  7. 小白算法练习 NYOJ 吝啬的国度 dfs+邻接表
  8. Linux 4.x MTD源码分析-cfi-flash设备probe过程分析
  9. powerShell、cmd中命令使用Mysql
  10. c语言游泳是怎么钓鱼的,钓鱼怎么找水层?看完这篇你就懂!