📖微前端
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
✨微前端特性
- 技术栈无关 主框架不限制接入应用的技术栈,子应用可自主选择技术栈
- 独立开发/部署 各个团队之间仓库独立,单独部署,互不依赖
- 增量升级 当一个应用庞大之后,技术升级或重构相当麻烦,而微应用具备渐进式升级的特性
- 独立运行时 微应用之间运行时互不依赖,有独立的状态管理
- 提升效率 应用越庞大,越难以维护,协作效率越低下。微应用可以很好拆分,提升效率
✈️qiankun 微前端安装以及使用
微前端分为主应用和子应用,主应用和子应用需要单独配置:
主应用部分
- 安装 qiankun
yarn add qiankun # 或者 npm i qiankun -S
- 注册子应用
import { registerMicroApps, start } from "qiankun";
// 注册子应用
registerMicroApps([
{
name: "app1", // 子应用名称
entry: "//localhost:8080", // 子应用的地址
container: "#container1", // 子应用需要渲染的dom节点
activeRule: "/yourActiveRule", // 子应用的匹配
},
{
name: "app2",
entry: "//localhost:8081",
container: "#container2",
activeRule: "/yourActiveRule2",
},
]);
start();
子应用部分
- 导出微应用相关的生命周期钩子
在入口文件中导出 bootstrap、mount、unmount 三个生命周期钩子,用于主应用调用
/*
bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log("app bootstraped");
}
// 应用每次进入都会调用 mount 方法,通常我们在这里触发子应用的渲染
export async function mount(props) {
ReactDOM.render(
<App />,
props.container
? props.container.querySelector("#root")
: document.getElementById("root")
);
}
/*
应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount(props) {
ReactDOM.unmountComponentAtNode(
props.container
? props.container.querySelector("#root")
: document.getElementById("root")
);
}
/*
仅使用 loadMicroApp 方式加载微应用时生效(该生命周期钩子可选)
*/
export async function update(props) {
console.log("update props", props);
}
- 入口文件修改实例渲染方法
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import routes from "./router";
import store from "./store";
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
let router = null;
let instance = null;
// 将渲染方法抽离
function render(props = {}) {
const { container } = props; // 从主应用获取在主营中渲染的dom节点
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? "/app-vue/" : "/",
mode: "history",
routes,
});
instance = new Vue({
router,
store,
render: (h) => h(App),
}).$mount(container ? container.querySelector("#app") : "#app"); // 根据是否为主应用加载还是独立运行来确定挂载的dom节点
}
// 独立运行时
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;
}
- 配置子应用的打包配置
Vue.config.js
const packageName = require("./package.json").name;
module.exports = {
output: {
library: `${packageName}-[name]`,
libraryTarget: "umd",
jsonpFunction: `webpackJsonp_${packageName}`,
},
};
🙋♂️注意问题
- 不同
vue-router版本路由修改:
// vue-router 3
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? "/app-vue1/" : "/",
mode: "history",
routes,
});
// vue-router 4
router = VueRouter.createRouter({
history: VueRouter.createWebHistory(
window.__POWERED_BY_QIANKUN__ ? "/app-vue2/" : "/"
),
routes,
});
- 当版本为
webpack5/vue-cli5的时候这样配置
由于
webpack5舍弃了jsonpFunction字段,需要更改为新的字段chunkLoading
const { defineConfig } = require("@vue/cli-service");
const name = require("./package.json").name;
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: "umd", // 把微应用打包成 umd 库格式
chunkLoading: "jsonp", // webpack5 舍弃了 jsonpFunction 配置,使用chunkLoading
},
},
});