乾坤(qiankun)的使用
官方网站
介绍
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);
}