微服务介绍
前端微服务是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
微服务的优点:
-
技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权 -
独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 -
增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
-
独立运行时
每个微应用之间状态隔离,运行时状态不共享
+++
微服务插件——qiankun.js
qiankun介绍
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
设计理念:
-
简单
由于主应用微应用都能做到技术栈无关,qiankun 对于用户而言只是一个类似 jQuery 的库,你需要调用几个 qiankun 的 API 即可完成应用的微前端改造。同时由于 qiankun 的 HTML entry 及沙箱的设计,使得微应用的接入像使用 iframe 一样简单。
-
解耦/技术栈无关
微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用,而 qiankun 的诸多设计均是秉持这一原则,如 HTML entry、沙箱、应用间通信等。这样才能确保微应用真正具备 独立开发、独立运行 的能力。
特性:
- 基于 single-spa 封装,提供了更加开箱即用的 API。
- 技术栈无关,任意技术栈的应用均可 使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。
- HTML Entry 接入方式,让你接入微应用像使用 iframe 一样简单。
- 样式隔离,确保微应用之间样式互相不干扰。
- JS 沙箱,确保微应用之间 全局变量/事件 不冲突。
- 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。
- umi 插件,提供了 @umijs/plugin-qiankun 供 umi 应用一键切换成微前端架构系统。
+++
代码演示(Vue+ Qiankun+React+static+Angular)
本案例使用一个基座加四个子应用,通过qiankun.js将两个项目连接起来。
https://www.jianshu.com/p/3484415c8a00
主应用(基座):
// /src/main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
import routes from './router'
import { registerMicroApps, addGlobalUncaughtErrorHandler, start } from "qiankun";
const apps = [
/**
* name: 微应用名称 - 具有唯一性
* entry: 微应用入口 - 通过该地址加载微应用
* container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
* activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
*/
{
name: "VueMicroApp",
entry: "//localhost:10200",
container: "#vue",
activeRule: "/vue"
},
];
// 注册
registerMicroApps(apps, {
// qiankun 生命周期钩子 - 加载前
beforeLoad: () => {
// 加载子应用前,加载进度条
console.log("before load");
return Promise.resolve();
},
// qiankun 生命周期钩子 - 挂载后
afterMount: () => {
// 加载子应用前,进度条加载完成
console.log("after mount")
return Promise.resolve();
},
});
addGlobalUncaughtErrorHandler((event) => {
console.error(event);
});
const router = new VueRouter({
mode:"history", // 这个是必须的
routes: routes
})
start()
new Vue({
router,
render: (h) => h(App),
}).$mount("#app");
子应用(vue):
import Vue from "vue";
import VueRouter from "vue-router";
if (window.__POWERED_BY_QIANKUN__) {
// 动态设置 webpack publicPath,防止资源加载出错
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
import App from "./App.vue";
import routes from "./routes";
Vue.use(VueRouter);
Vue.use(Antd);
Vue.config.productionTip = false;
let instance = null;
let router = null;
/**
* 渲染函数
* 两种情况:主应用生命周期钩子中运行 / 微应用单独启动时运行
*/
function render() {
// 在 render 中创建 VueRouter,可以保证在卸载微应用时,移除 location 事件监听,防止事件污染
router = new VueRouter({
// 运行在主应用中时,添加路由命名空间 /vue
base: window.__POWERED_BY_QIANKUN__ ? "/vue" : "/",
mode: "history",
routes,
});
// 挂载应用
instance = new ({
router,
render: (h) => h(App),
}).$mount("#app");
}
// 独立运行时,直接挂载应用
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log("VueMicroApp bootstraped");
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
console.log("VueMicroApp mount", props);
render(props);
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount() {
console.log("VueMicroApp unmount");
instance.$destroy(); // 销毁实例,避免内存泄露
instance = null;
router = null;
}
// vue.config.js
const path = require("path");
module.exports = {
configureWebpack: {
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
},
// 这里子应用暴露出去,是必须的
output: {
library: 'VueMicroApp', // 微应用的包名,这里与主应用中注册的微应用名称一致
libraryTarget: 'umd', // 将你的 library 暴露为所有的模块定义下都可运行的方式
jsonpFunction: `webpackJsonp_VueMicroApp`, // 按需加载相关,设置为 webpackJsonp_VueMicroApp 即可
}
},
devServer: {
port: 10200, // 监听端口
open: true,
disableHostCheck: true,// 关闭端口检查
headers: { // 配置跨域请求头,关闭跨域
'Access-Control-Allow-Origin': "*",
}
},
};