介绍
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
微前端架构具备以下几个核心价值:
- 技术栈无关
主框架不限制接入应用的技术栈,微应用具备完全自主权 - 独立开发、独立部署
微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新 - 增量升级
在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略 - 独立运行时
每个微应用之间状态隔离,运行时状态不共享
微前端架构旨在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级 Web 应用中尤其常见。
实现
这里创建 vue2 项目作为主应用:
vue create main
安装 qiankun
yarn add qiankun
npm i qiankun -S
这里展现一个菜单项渲染一个微应用
/router/index.js :
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Layout from '../components/Layout'
Vue.use(VueRouter)
const routes = [
{
path: '/',
component: Layout,
redirect: '/index',
children: [
{
path: 'index',
component: HomeView
}
],
},
// 微应用的路由访问的时候 主路由需要有对应的配置渲染layout
{
path: '/mic-*',
component: Layout
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
在主应用中注册微应用
/components/Layout.vue :
<template>
<div class="main">
<div class="topnav"></div>
<div class="mainbt">
<div class="menu">
<div>
<router-link to="/mic-vue2">vue2 应用</router-link>
</div>
<div>
<router-link to="/mic-vue3">vue3 应用</router-link>
</div>
</div>
<div class="content">
<router-view />
<div id="mcapp"></div>
</div>
</div>
</div>
</template>
<script>
import { registerMicroApps, start } from 'qiankun';
export default {
mounted() {
// 在vue挂载完成之后再去注册乾坤的微应用
const apps = [
{
name: 'vue2', // nane确定唯一
entry: '//localhost:5001', // 应用的入口链接
container: '#mcapp', // 微应用要挂载的节点位置
activeRule: '/mic-vue2', // 微应用的路由激活路径 主应用当中访问该路由会 挂载对应的微应用
},
{
name: 'vue3',
entry: '//localhost:5002',
container: '#mcapp',
activeRule: '/mic-vue3',
}
];
registerMicroApps(apps); // 注册微应用
start(); // 开始启用乾坤
}
}
</script>
<style>
body {
margin: 0;
}
.main {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.topnav {
height: 80px;
border: 1px solid #000;
}
.mainbt {
height: 0;
flex-grow: 1;
display: flex;
}
.menu {
width: 220px;
border: 1px solid red;
}
.content {
width: 0;
flex-grow: 1;
border: 2px solid blue;
}</style>
当微应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。
创建一个 vue2 项目作为微应用 :
vue create vueproject
微应用不需要额外安装任何其他依赖即可接入 qiankun 主应用。
在配置的 webpack 的 入口 js 文件导出相应的生命周期钩子
/src/main.js :
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
Vue.config.productionTip = false
// 作为乾坤的子应用不能自己 立即实例化 要使用乾坤子应用对应的生命周期中去实例化
function render() {
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
}
/**
* bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
*/
export async function bootstrap() {
}
/**
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
*/
export async function mount(props) {
render();
}
/**
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
*/
export async function unmount(props) {
}
/**
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
*/
export async function update(props) {
console.log('update props', props);
}
配置微应用的打包工具
除了代码中暴露出相应的生命周期钩子之外,为了让主应用能正确识别微应用暴露出来的一些信息,微应用的打包工具需要增加如下配置
webpack v5 :
vue.config.js :
const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package')
module.exports = defineConfig({
transpileDependencies: true,
publicPath: './',
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 乾坤的子应用要打包成umd的模式
chunkLoadingGlobal: `webpackJsonp_${name}`
}
},
devServer: {
port: 5001, // 指定子应用的端口
headers: { // 支持静态资源引入的时候的跨域配置
'Access-Control-Allow-Origin': '*'
}
}
})
在 src 目录新增 public-path.js :
// 收到的去修改 webpack打包文件的基础依赖路径
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
创建一个 vue3 项目作为微应用 :
vue create vueapp
vue.config.js :
const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package')
module.exports = defineConfig({
transpileDependencies: true,
publicPath: './',
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 乾坤的子应用要打包成umd的模式
chunkLoadingGlobal: `webpackJson_${name}`
}
},
devServer: {
port: 5002,
headers: {
'Access-Control-Allow-Origin': '*'
}
}
})
/src/public-path.js :
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path_ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
/src/main.js :
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
function render() {
createApp(App).use(store).use(router).mount('#app')
}
export async function bootstrap() {
}
export async function mount() {
render();
}
export async function unmount() {
}
export async function update(props) {
console.log('update props', props);
}