文章目录
介绍
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
官网文档:https://qiankun.umijs.org/zh
什么是微前端
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
微前端架构具备以下几个核心价值:
- 技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权 - 独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 - 增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略 - 独立运行时
每个微应用之间状态隔离,运行时状态不共享
提示:以下是基于vue2搭建的qiankun微前端
开始
首先创建两个项目,一个作为项目基座(vue-qiankun-base),一个作为子应用(vue-qiankun-app1)
一、主应用搭建
1.安装qiankun
npm i qiankun -S
2.项目导入
main.js
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'app1',
entry: '//192.168.100.5:82/qiankunchild/',// 运行微应用的地址
container: '#app1',// 展示的DOM容器 id
activeRule: '/',// 跳转路径
props:{
token: '',
}// 通过props实现通信传递值
},
]);
// 启动 qiankun
start({prefetch:false});
3.子应用挂在DOM位置
App.vue
<div id="app">
<div id="app1"></div>/*注释:这里的app1对应main.js中registerMicroApps的container*/
</div>
到此为止主应用配置完成
二、微应用搭建
1.src下创建public-path.js
为子应用环境变量,用于判断主应用进入时的url地址
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
2.main.js配置
import './public-path';
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import routes from './router';
import store from './store';
Vue.config.productionTip = false;
let router = null;
let instance = null;
function render(props = {}) {
const { container } = props;
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/qiankunchild/' : '/',
mode: 'history',
routes,
});
instance = new Vue({
router,
store,
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
// 独立运行时
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.$el.innerHTML = '';
instance = null;
router = null;
}
3.修改vue.config.js
const { packageName } = require('./package');
module.exports = {
publicPath:"/qiankunchild",
devServer: {
port: 82,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${packageName}`,
},
},
};
问题总结
1.主、子应用路由模式选择问题
(1)主应用hash模式 + 微应用hash模式
当主应用是 hash 模式时,一般微应用也是 hash 模式。主应用的一级 hash 路径会分配给对应的微应用(比如 #/app1 ),此时微应用如果需要在 base 路径的基础上进行 hash 模式下的二级路径跳转(比如 #/app1/child1 ),这个场景在当前 VueRouter 的实现方式下需要自己手动实现,给所有路由都添加一个前缀即可。一般推荐
//-------------------------------主应用-----------------------------------------
//1.router/index.js (路由配置)
const router = new VueRouter({
// mode: 'history',
mode: 'hash',
base: process.env.BASE_URL,
routes
})
//2.main.js 配置:
//qiankun 主应用根据 activeRule 配置激活对应微应用
//激活路由需要添加#/前缀
registerMicroApps([
{
name: 'vueApp',
entry:'//192.168.100.5:82',
container: '#container',
activeRule: '#/app-vue',//激活路径
props: {},
},
]);
//-------------------------------子应用------------------------------------
//3.子级vue应用配置:hash模式
//需要在每个路由里面添加激活前缀/app-vue
//注意这里不能加 #/app-vue这个前缀,前缀跟主应用的激活路由匹配
const routes = [
{
path: '/app-vue',
name: 'Home',
meta: {
title: '案例1',
},
component: (resolve) => require(['@/pages/demo_01'], resolve),
},
{
path: '/app-vue/about',
name: 'demo2',
meta: {
title: '案例2',
},
component: (resolve) => require(['@/pages/demo_02'], resolve),
},
];
router = new VueRouter({
base: process.env.BASE_URL,
mode: 'hash',
routes,
});
(2)主应用history模式 + 微应用hash模式
体验一般,不推荐
//-------------------------------主应用-----------------------------------------
//1.router/index.js (路由配置)
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
//2.main.js 配置:
//qiankun 主应用根据 activeRule 配置激活对应微应用
//激活路由不需要添加#/前缀
registerMicroApps([
{
name: 'vueApp',
entry:'//192.168.100.5:82',
container: '#container',
activeRule: '/app-vue',//激活路径
props: {},
},
]);
//-------------------------------子应用------------------------------------
//3.子级vue应用配置:hash模式
//需要在每个路由里面添加激活前缀/app-vue
const routes = [
{
path: '/app-vue',
name: 'Home',
meta: {
title: '案例1',
},
component: (resolve) => require(['@/pages/demo_01'], resolve),
},
{
path: '/app-vue/about',
name: 'demo2',
meta: {
title: '案例2',
},
component: (resolve) => require(['@/pages/demo_02'], resolve),
},
];
router = new VueRouter({
base: process.env.BASE_URL,
mode: 'hash',
routes,
});
(3)主应用history模式 + 微应用history模式
体验感完美,推荐
//-------------------------------主应用-----------------------------------------
//1.router/index.js (路由配置)
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
//2.main.js 配置:
//qiankun 主应用根据 activeRule 配置激活对应微应用
//激活路由不需要添加#/前缀
registerMicroApps([
{
name: 'vueApp',
entry:'//192.168.100.5:82',
container: '#container',
activeRule: '/app-vue',//激活路径
props: {},
},
]);
//-------------------------------子应用------------------------------------
//3.子级vue应用配置:history模式
const routes = [
{
path: '/',
name: 'Home',
meta: {
title: '案例1',
},
component: (resolve) => require(['@/pages/demo_01'], resolve),
},
{
path: '/about',
name: 'demo2',
meta: {
title: '案例2',
},
component: (resolve) => require(['@/pages/demo_02'], resolve),
},
];
router = new VueRouter({
base: '/app-vue',
mode: 'history',
routes,
});
2.主、子应用是不同的后台,子应用访问接口时会默认走主应用端口导致接口404
解决方案:vue.config.js下加上子应用的代理即可
const { packageName } = require('./package');
module.exports = {
publicPath:"/qiankunchild",
devServer: {
port: 82,
headers: {
'Access-Control-Allow-Origin': '*',
},
proxy: {
"/upload": { // 子应用代理
target: "",
changeOrigin: true,
secure: false,
pathRewrite: {
"^/upload": "/",
},
},
}
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${packageName}`,
},
},
};
3.原主项目下,开发子应用,怎么不应用原项目
解决方案:配置独立页面
const packageName = require("./package.json").name;
const pagesObject = {
main: { //原项目
entry: "./src/main.js",
template: "public/index.html",
filename: "index.html",
},
childApp:{ //子应用
entry: './src/page/childApp/main.js',
template: 'public/childApp.html',
filename: 'childApp.html'
}
};
module.exports = {
pages: pagesObject,
configureWebpack: {
output: {
library: `${packageName}-[name]`,
libraryTarget: "umd", // 把微应用打包成 umd 库格式
jsonpFunction: `webpackJsonp_${packageName}`,
},
},
}