Micro-frontend,微前端,顾名思义,前端也可以像后端一样通过多个微服务,搭建项目。只是后端的微服务架构是Spring Boot, 而前端我们可以使用qiankun实现前端的微服务架构。轻松现实大型项目的迭代重构升级以及拓展,不受牵一发而动全身的限制。下面介绍简单搭建微前端框架。
项目下载地址 demo
├── base # 主服务 - 可以是任意框架vue/react/angular/原生....
├── public # 资源
└── src # 主要code
└── main.js 配置注册微服务信息,以及给子应用传参等...
├── app1-vue # 微服务app1 - vue
├── public # 资源
├── src # 主要code
└── main.js # 配置 注册信息 bootstrap/mount/unmount 方法声明
└── vue.config.js # 配置允许跨域,以及端口
├── app2-react # 微服务app2 - react
├── public # 资源
└── src # 主要code
└── index.js 配置 注册信息 bootstrap/mount/unmount 方法声明
└── config-overrides.js # 配置允许跨域,以及端口
1.创建vue主应用
创建Vue工程作为主服务,当然也可以是任意框架vue/react/angular/原生项目作为主应用。
vue create base
安装 qiankun
npm install qiankun -s
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
import {
registerMicroApps, // 注册应用
start, // 开启方法
} from 'qiankun';
const apps = [
{
name: 'vueApp', // 应用名字,这个是微应用app1
entry: '//localhost:10000', //默认会加载这个html 解析里面的js(这里使用的fetch) 动态的执行(子应用必须支持跨域)
container: '#vue', //容器名 挂载元素
activeRule: '/vue', // 激活路径 激活规则,当访问 /vue时就会把这个应用挂载到 #vue上
props: { a: 1 }, // 实现主应用给子应用传参
},
{
name: 'reactApp', // 应用名字,这个是微应用app2
entry: '//localhost:20000',
container: '#react',
activeRule: '/react',
props: { b: 2 }, // 实现主应用给子应用传参
},
];
registerMicroApps(apps); // 注册应用
start({
// 开启
prefetch: false, // 取消预加载
});
Vue.config.productionTip = false;
new Vue({
router,
render: h => h(App),
}).$mount('#app-base'); // 这里需要更改挂载节点,防止跟app1-vue的节点重名。
注意:根节点与Vue子应用重名将会报错,这里将根节点改为app-base,对应的index.html和app.vue也需要将id="app"改为id=“app-base”。具体改动如下:
改成
改成
public/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"></div> -->
<div id="app-base"></div> // 这里需要把app改成app-base,防止跟app1-vue的挂载节点重名
<!-- built files will be auto injected -->
</body>
</html>
App.vue中需要改动如下:
<template>
<div id="app-base">
<img alt="Vue logo" src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<el-menu default-active="/" :router="true" mode="horizontal">
<el-menu-item index="/">base</el-menu-item>
<el-menu-item index="/vue">vue应用</el-menu-item>
<el-menu-item index="/react">react应用</el-menu-item>
</el-menu>
<router-view></router-view>
<div id="vue"></div>
<div id="react"></div>
</div>
</template>
2.创建app1-vue 子应用
按照上述创建主应用Vue工程创建一个vue项目
注意 vite目前暂不支持qiankun !!!
运行时报错
[import-html-entry]: error occurs while executing entry script http://localhost:40000/src/main.ts?t=1678894335319
vue create app1-vue
在跟目录下添加配置文件 vue.config.js
配置如下:
module.exports = {
devServer: {
port: 10000, // 跟父应用配置的子应用端口一致
headers: {
// 因为qiankun内部请求都是fetch来请求资源,所以子应用必须允许跨域
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
//资源打包路径
library: 'vueApp',
libraryTarget: 'umd',
},
},
};
配置src/main.js
父应用之所可以加载子应用,是因为子应用必须暴露三个接口:bootstrap,mount,unmount。
import Vue from 'vue';
import App from './App.vue';
// Vue.config.productionTip = false
let instance = null;
function render(props) {
instance = new Vue({
render: h => h(App),
}).$mount('#app'); // 这里是挂载到自己的html中 base会拿到这个挂载后的html 将其插入进去
}
// 动态添加publicPath
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// 默认独立运行
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
// 父应用加载子应用,子应用必须暴露三个接口:bootstrap,mount,unmount
/**
* required
* bootstrap z只会在微应用初始化时调用一次,下次微应用重新进入时会直接调用mount钩子,不会再重复触发,bootstrap.
* 通常我们可以在这里做一些全局变量的初始化,比如不会在unmount 阶段被销毁的应用级别的缓存等。
*
*/
export async function bootstrap(props) {
console.log('bootstrap:', props);
}
/**
* required
* 应用每次进入都会调用mount方法,通常我们在这里触发应用渲染方法
*/
export async function mount(props) {
console.log('mount:', props);
render(props);
}
/**
* required
* 应用每次 切出/卸载 都会调用的方法
* 通常我们会卸载微应用的应用实例
*/
export async function unmount(props) {
console.log('unmount:', props);
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
}
/**
* not required
* 仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
console.log('update props', props);
}
3.创建app2-react 子应用
npm install -g create-react-app
create-react-app app2-react
src/index.js中配置挂载信息,暴露父应三个接口:bootstrap,mount,unmount。
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';
// import reportWebVitals from './reportWebVitals';
// const root = ReactDOM.createRoot(document.getElementById('root'));
// root.render(
//
//
//
// );
// 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();
const root = ReactDOM.createRoot(document.getElementById('root'));
function render() {
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// ReactDOM.render(
//
//
// ,
// document.getElementById('root')
// );
}
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap(props) {
console.log('bootstrap:', props);
}
export async function mount(props) {
console.log('mount:', props);
render();
}
export async function unmount(props) {
console.log('unmount:', props);
// 卸载节点
// ReactDOM.unmountComponentAtNode(document.getElementById('root'));
root.unmount();
}
项目引入配置依赖react-app-rewired
npm install react-app-rewired -D
在根目录添加config-overrides.js,配置如下:
module.exports = {
webpack: config => {
config.output.library = 'reactApp';
config.output.libraryTarget = 'umd';
config.output.publicPath = 'http://localhost:20000'; // 此应用自己的端口号
return config;
},
devServer: configFunction => {
return function (proxy, allowedHost) {
const config = configFunction(proxy, allowedHost);
config.port = '20000';
config.headers = {
'Access-Control-Allow-Origin': '*',
};
return config;
};
},
};
在package.json
中修改启动方式如下:
"scripts": {
"start": "PORT=20000 react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
运行项目
1.运行主项目
cd base
npm run serve
2.运行子应用
cd app1-vue
npm run serve
3.运行子应用
cd app1-react
npm run start
主应用配置的vue子应用的运行端口为10000,react子应用的端口号为20000,三个项目分别启动成功后就可以通过主应用来访问了。这时子应用就会加载到主应用里面。子应用也可以通过各自的端口单独访问。