概念
使用框架:qiankun
介绍:
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
qiankun官网:Go
当使用 iframe 整合系统时,假设我们有系统 A, 当我们想把系统 B 引入 A 系统时,只需要 B 系统提供一个 url 给 A 系统引用即可,这里我们把 A 系统叫做父应用,把 B 系统叫做子应用。同样的,微前端也延续了这个概念,微前端在使用起来基本和使用 iframe 一样平滑。
结构:
分为:主应用(父),微应用(子)
图1:
使用案例:
目标:
点击one按钮,切换到one微应用
点击two按钮,切换到two微应用
并且每个微应用都可以跳转自己的路由
步骤:
- 创建主应用项目
- 创建微应用项目
- 将微应用挂到主应用项目中
1. 在主应用中安装qiankun框架
$ yarn add qiankun # 或者 npm i qiankun -S
2. 在 主应用 中注册微应用
main.js:
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: "vueChildOne",
props: { age: 10 }, //给子应用传数据
entry: "//localhost:3001", //默认会加载这个html,解析里面的js,动态执行(子应用必须支持跨域)里面,是用fetch去请求的数据
container: "#child-one-content", //挂载到主应用的哪个元素下
activeRule: "/child-one", //当我劫持到路由地址为//child-one时,我就把http://localhost:3001这个应用挂载到#child-one-content的元素下
},
{
name: 'vueChildTwo',
entry: '//localhost:3002',
// entry: { scripts: ['//http://192.168.2.120:3001/main.js'] },
container: '#child-two-content',
activeRule: '/child-two',
},
]);
// start(); // 启动qiankun,这里不在一开始的时候使用
createApp(App).use(ElementPlus).use(router).mount('#app-base')
App.vue
<template>
<div>
<div class="bens">
<el-button type="primary" @click="$router.push('/child-one')">childOne</el-button>
<el-button type="primary" @click="$router.push('/child-two')">childTwo</el-button>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "App",
components: {},
};
</script>
<style>
.bens {
width: 100%;
display: flex;
justify-content: center;
position: absolute;
top: 15px;
left: 0;
z-index: 9999999;
}
#app-base {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
index.html:
// 将id:app 改为 app-base 自定义就行,只要与main.js对应起来,切不与微应用重复
<div id="app-base"></div>
router.js
import { createRouter, createWebHistory } from "vue-router";
// 2. 配置路由
const routes = [
{
path: '/child-one',
component: () => import('@/views/childOne/index.vue'),
},
{
path: '/child-two',
component: () => import('@/views/childTwo/index.vue'),
},
];
// 1.返回一个 router 实列,为函数,里面有配置项(对象) history
const router = createRouter({
history: createWebHistory(),
routes,
});
// 3导出路由 然后去 main.ts 注册 router.ts
export default router
childOne/index.vue
<template>
<h2>我是 主应用 one</h2>
<div id="child-one-content"></div>
</template>
<script>
import { start } from "qiankun";
export default {
name: "childOne",
components: {},
mounted() {
// 启动微应用
if (!window.qiankunStarted) {
window.qiankunStarted = true;
start();
}
},
};
</script>
<style>
</style>
childTwo/index.vue
<template>
<h2>我是 主应用 two</h2>
<div id="child-two-content"></div>
</template>
<script>
import { start } from "qiankun";
export default {
name: "childTwo",
components: {},
mounted() {
// 启动微应用
if (!window.qiankunStarted) {
window.qiankunStarted = true;
start();
}
},
};
</script>
<style>
</style>
4. 微应用配置child-one
src下创建public-path.js
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import './public-path' // 引入public-path.js
// createApp(App).mount('#app')
let instance = null;
function render(props = {}) {
if (instance) return;
const { container } = props;
console.log(container);
instance = createApp(App)
.use(router)
.mount(container ? container.querySelector("#app-child-one") : "#app-child-one");
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log("[vue] vue app bootstraped");
}
export async function mount(props) {
console.log("[vue] props from main framework", props);
render(props);
}
export async function unmount() {
//可选链操作符
instance.$destroy?.();
instance = null;
}
index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app-child-one"></div>
<!-- built files will be auto injected -->
</body>
</html>
vue.config.js
module.exports = {
lintOnSave: false,
devServer: {
port: "3001",
headers: {
"Access-Control-Allow-Origin": "*", //所有人都可以访问我的服务器
},
},
configureWebpack: {
output: {
// library: `${name}-[name]`,
library: `vueChildOne`,
libraryTarget: "umd", // 把微应用打包成 umd 库格式
// jsonpFunction: `webpackJsonp_${name}`,
},
},
};
router.js
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
// 2. 配置路由
const routes = [
{
path: '/',
component: () => import('@/views/home/index.vue'),
},
{
path: '/about',
component: () => import('@/views/about/index.vue'),
},
];
// 1.返回一个 router 实列,为函数,里面有配置项(对象) history
const router = createRouter({
history: createWebHashHistory('/child-one'),
routes,
});
// 3导出路由 然后去 main.ts 注册 router.ts
export default router
5. 微应用配置child-two
与child-two同理,只要把对应的" child-one " 换成" child-two "即可
demo代码地址
码云地址