前言
开发中常见的问题
- 以一个ERP应用举例,系统中包含销售、财务、人事等等多个细分模块存在。
当我们改动任意一个模块的代码时,都需要将整个应用跑起来,包括当前用不到的部分。
相对应的,我们需要重新更新部署整个应用; - 想升级新技术,但是苦于应用的原技术过于老旧且体积巨大,不能一次性升级。
什么是微前端
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
通俗来讲,微前端这一架构,使我们可以将庞大的应用拆分为多个小型应用,这些应用可以独立的运行开发和部署,最终这些小型应用组合为一个完整的应用。
微前端架构的核心价值
-
技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权 -
独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 -
增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,
而微前端是一种非常好的实施渐进式重构的手段和策略 -
独立运行时
每个微应用之间状态隔离,运行时状态不共享
qiankun
介绍
官网:qiankun - qiankun (umijs.org)
qiankun是一个基于single-spa的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
qiankun 的核心设计理念
-
🥄 简单
由于主应用微应用都能做到技术栈无关,qiankun 对于用户而言只是一个类似 jQuery 的库,你需要调用几个 qiankun 的 API 即可完成应用的微前端改造。同时由于 qiankun 的 HTML entry 及沙箱的设计,使得微应用的接入像使用 iframe 一样简单。 -
🍡 解耦/技术栈无关
微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用,而 qiankun 的诸多设计均是秉持这一原则,如 HTML entry、沙箱、应用间通信等。这样才能确保微应用真正具备 独立开发、独立运行 的能力。
qiankun微前端架构分为主应用和微应用;
主应用:可以理解为一个底座,所有的子应用在这里组成一个完整的应用;
微应用:子应用,是一个个独立的应用;
主应用部分
安装 qiankun
(注意,只需要在主应用中安装就行了,微应用不需要安装)
npm i qiankun
添加挂载微应用的节点
<template>
<div class="layout">
<!-- 这是你主应用的渲染容器,应该不用多说 -->
<router-view v-show="!isMicroApp"/>
<!-- 这就是要增加的节点,用于挂载微应用 -->
<div id="qiankunView" v-show="isMicroApp"></div>
</div>
</template>
export default {
computed: {
// 判断路由属于子应用还是主应用
isMicroApp() {
return this.$route.path.indexOf('/demo-') > -1
}
},
}
注册微应用
(可以直接写在main.js文件中;也可以抽离到单独的文件里,然后在main.js中引入。)
import { registerMicroApps, start } from 'qiankun';
registerMicroApps([
{
name: 'demo1', // 微应用名字
entry: '//localhost:3000', // 微应用的入口
container: '#qiankunView', // 微应用挂载的节点(要跟主应用的挂载节点区分开)
activeRule: '/#/demo-1/', // 对url做前缀匹配,匹配成功会激活微应用
props: {
// 在这里可以给微应用传递数据
}
},
{
name: 'demo2',
entry: '//localhost:3001',
container: '#qiankunView',
activeRule: '/#/demo-2/',
},
]);
start();
微应用部分
新建 public-path.js 文件
新建 public-path.js 文件,并引入到 main.js 中。(当然也可以直接写到main.js里)
__webpack_public_path__ 是webpack 暴露的一个全局变量,用于在运行时设置 publicPath。公共路径
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
main.js 改造
微应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出
bootstrap、mount、unmount三个生命周期钩子,以供主应用在适当的时机调用。
import './public-path';
import Vue from 'vue';
import routes from '@/routes';
import App from './App.vue';
let router = null;
let instance = null;
function render(props = {}) {
const { container } = props;
router = routes;
instance = new Vue({
router,
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
// 独立运行时(你单独跑这个项目的时候)
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
/**
* bootstrap 只在微应用初始化的时候调用一次,下次重新进入时会直接调用 mount 钩子,不会再重复触发。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
// 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
export async function mount(props) {
// 这里可以接收主应用传递的数据
console.log("%c%s", "color: green;", 'demo1启动');
render(props);
}
// 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
export async function unmount() {
console.log("%c%s", "color: red;", '卸载demo1');
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
router = null;
}
vue.config.js 改造
const { name } = require('./package');
module.exports = {
transpileDependencies: true,
publicPath: './',
devServer: {
port: 3000, // 运行端口号
headers: {
'Access-Control-Allow-Origin': '*', // 本地开发跨域问题
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
chunkLoadingGlobal: `webpackJsonp_${name}`,
},
},
}
基本使用就是这样,拜了个拜!