前言
微前端类似于微服务,实质上是将一个前端应用变为由多个独立子应用集合而成的应用。
其中每一个子应用都可以独立开发,独立部署,并且对技术栈没有要求。
目前个人使用的微前端框架是qiankun和wujie。
qiankun是蚂蚁金服基于Single SPA开发的一个微前端实现库。
wujie是一款基于 Web Components + iframe 微前端框架。
这里先记录一下qiankun的使用.
qiankun官网
一、主应用
主应用负责管理子应用页面的跳转
1.安装qiankun框架
npm i qiankun
2.路由配置
import { createRouter, createWebHistory } from 'vue-router';
const routes = [];
const router = createRouter({
history: createWebHistory('/'),
routes,
});
export default router
3.在main.js中注册路由和子应用
import { createApp } from 'vue'
import App from './App.vue'
import { registerMicroApps, start } from 'qiankun';
import router from '@/router/index'
registerMicroApps([
{
name: 'test-1', //子应用1,全新的空白应用,vue2+js
props: {}, //给子应用传递数据
entry: '//localhost:8082', //子应用入口
container: '#main', //子应用容器
activeRule: '/test-1', //子应用激活规则,主应用通过该字段判断是否切换子应用
},
{
name: 'vite-project', //子应用2,已经写过的demo,vue3+ts+vite
props: {},
entry: '//localhost:8085',
container: '#main',
activeRule: '/vite-project',
},
]);
start(); //启动
createApp(App).use(router).mount('#main')
4.在App.vue中添加两个跳转按钮
<!--
* @Description: 主应用界面
-->
<template>
<div id="main">
<div class="bens">
//主应用跳转按钮
<button type="primary" @click="$router.push('/test-1/app')">test-1</button>
<button type="primary" @click="$router.push('/vite-project/Home')">vite-project</button>
</div>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: "App",
components: {},
mounted() {},
};
</script>
<style>
#main {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
height: 100%;
width: 100%;
}
#__qiankun_microapp_wrapper_for_vite_project__ {
height: 100%;
width: 100%;
}
</style>
二、子应用test-1
1.index.html添加容器id,需与在主应用注册时的container一致
<!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>
//添加id
<div id="test-1"></div>
</body>
</html>
2.在src目录下添加public-path.js文件
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
3.在main.js中注册生命周期并引入public-path.js文件
import { createApp } from 'vue'
import App from './App.vue'
import router from './router/index'
import './public-path' // 引入public-path.js
let instance = null;
// 限制子应用id查找范围,防止微应用的根 id 与其他 DOM 冲突
function render(props = {}) {
if (instance) return;
const { container } = props;
instance = createApp(App)
.use(router)
.mount(container ? container.querySelector("#test-1") : "#test-1");
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
//仅在初始化时调用一次
export async function bootstrap() {}
//每次进入子应用都会调用
export async function mount(props) {
render(props);
}
//切出/卸载时调用
export async function unmount() {
instance.$destroy?.();
instance = null;
}
4.修改vue.config.js文件
const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package.json');
module.exports = defineConfig({
publicPath: '/',
transpileDependencies: true,
configureWebpack: {
plugins: [],
output: {
// 把子应用打包成 umd 库格式
library: `${name}-[name]`,
libraryTarget: 'umd',
chunkLoadingGlobal: `webpackJsonp_${name}`,
},
},
devServer: {
port: 8082,
headers: {
//解决主应用跳转子应用出现的跨域问题
'Access-Control-Allow-Origin': '*'
},
proxy: {
'/dev-api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: p => p.replace(/^\/dev-api/, '')
}
}
}
})
5.修改路由
import { createRouter, createWebHistory } from 'vue-router';
const routes = [
{
path: '/app',
name: 'app',
component: () => import('@/views/app.vue'),
},
];
// 1.返回一个 router 实列,为函数,里面有配置项(对象) history
const router = createRouter({
//基础路径需与主应用中注册的激活规则保持一致
history: createWebHistory('/test-1'),
routes,
});
export default router
三、子应用vite-project
基本步骤与另一个子应用一致,部分需要修改
该子应用使用的是vite,而qiankun目前不支持vite,需要使用vite-plugin-qiankun插件
1.安装并使用vite-plugin-qiankun
npm i vite-plugin-qiankun
该插件需在vite.config.ts配置,具体配置最后一起展示
2.index.html添加容器id(同test-1)
3.修改main.ts
import { createApp } from 'vue';
import App from './App.vue';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import Vue from 'vue';
import router from './router';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
const app = createApp(App);
let instance = null as any;
function render(props = {}) {
if (instance) return;
const { container } = props as any;
const app = createApp(App);
instance = app
.use(ElementPlus, { zIndex: 3000 })
.use(router)
.mount(container ? container.querySelector('#vite-project') : '#vite-project');
}
// 独立运行时
if (!(window as any).__POWERED_BY_QIANKUN__) {
}
export const bootstrap = () => {
console.log('[vue] vue app bootstraped');
};
export async function mount(_props: any) {
console.log('mount', _props);
render(_props.container);
}
export async function unmount() {
//可选链操作符
instance.$destroy?.();
instance = null;
}
export async function update() {}
const initQianKun = () => {
// QiankunLifeCycle
renderWithQiankun({
mount,
bootstrap,
unmount,
update
});
};
qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun() : render();
4.不需要public-path.js
网上查询时说vite也需要pablic-path.js文件,实际上我尝试时删了也没影响
5.修改vite.config.ts
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import commonjs from '@rollup/plugin-commonjs';
import requireTransform from 'vite-plugin-require-transform';
import path from 'path';
//qiankun vite插件
import qiankun from 'vite-plugin-qiankun';
export default defineConfig({
// publicDir: 'http://localhost:8085',
// base: 'http://localhost:8085',
plugins: [
commonjs() as any,
vue(),
qiankun('vue3', { useDevMode: true }),
requireTransform({
fileRegex: /.js$|.vue$/
})
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src') // 别名
}
},
optimizeDeps: {
include: ['@/../lib/vform/designer.umd.js'] //此处路径必须跟main.js中import路径完全一致!
},
build: {
/* 其他build生产打包配置省略 */
//...
commonjsOptions: {
include: /node_modules|lib/ //这里记得把lib目录加进来,否则生产打包会报错!!
},
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
},
server: {
port: 8085,
headers: {
'Access-Control-Allow-Origin': '*'
},
//解决静态资源报错
origin: 'http://localhost:8085',
proxy: {
'/dev-api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: p => p.replace(/^\/dev-api/, '')
}
}
}
});
6.修改路由(同test-1)
结果
8081是主应用端口,当qiankun监听到 test-1 时就会跳转到对应子应用
所有子应用都通过子应用间接调用,会出现静态资源404等问题也是因为在主应用找不到子应用资源
总结
实际上还是有些麻烦,最近尝试了一下wujie确实要简单很多,只是没有在具体项目中使用可能会遇到未知问题,我当前项目还是用的qiankun,之后看有没有时间再写一篇wujie的。
出现问题可以去官网找,其次是官网github的讨论区。