qiankun 是什么
qiankun 是基于 single-spa 的微前端实现库,可以帮助大家更快速的构建一个前端微架构体系。
微前端是什么
官方给出的概念是:微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。简单理解就是一个大型项目中,有一个主应用,N 个子应用,各个应用之间可以独立开发,独立运行,独立部署。
为什么要用微前端
我们应该都有这样的经历,一个项目一直在更新迭代,参与的人也越来越多,代码也越来越多,导致项目越来越不好管理,打包速度越来越慢,打包体积越来越大,部署不方便。尤其是在有一个很小的需求要更新时,我们需要重新打包部署整个项目,就很头大。所以我们就需要微前端,把一个单一的应用,转换为多个可以独立开发、测试、部署的小应用。但用户是无感知的,依旧认为是一个产品。
开始搭建微前端的主应用(基座)
安装 qiankun
yarn add qiankun # 或者 npm i qiankun -S
在主应用中注册微应用
我们在 src 文件夹下新建一个 qiankun/index.ts ,用来放我们的 qiankun 的配置信息
// src/qiankun/index.ts
import {registerMicroApps, start} from 'qiankun';
registerMicroApps([
{
name: 'qiankun-demo-b', // 子应用的注册名称,即子应用 package.json 中的 name
entry: '//localhost:5174', // 子应用的启动地址
container: '#app-b', // 承载子应用的容器,在主应用中,有一个 ID 为 app-b 的 div,用来承载子应用
activeRule: '/about', // 点击哪个路由会进入子应用
},
/*
* 比如,我在 app.vue 文件中注册了两个路由
* <template>
<nav>
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
</nav>
<RouterView />
</template>
* 那么这个 activeRule: '/about' 对应的就是 <RouterLink to="/about">About</RouterLink> 这个路由
* 之后,我在 about.vue 这个文件中,添加一个承载子应用的 div,那么这个 container: '#app-b' 对应的就是 <div id="app-b"></div> 这个 div
* <template>
<div class="about">
<h1>This is an about page 111</h1>
<div id="app-b"></div>
</div>
</template>
* 有多个子应用,可以按这种方式来配置多个
* */
{
name: 'qiankun-demo-d',
entry: '//localhost:8080',
container: '#app-d',
activeRule: '/car',
}
]);
start();
在 main.ts 中引入
// 引入qiankun
import './qiankun/index'
微应用
Vue3 + Vite
我们先来看 Vue 微应用,Vue 微应用有一个点需要注意的就是 Vite , qiankun 与 Vite 不能一起使用,所以需要额外安装一个插件,以下是使用 Vite 时 qiankun 的配置
npm install vite-plugin-qiankun
微应用需要在自己的入口 js 导出 bootstrap
、mount
、unmount
三个生命周期钩子,以供主应用在适当的时机调用。
我们来修改子应用里的 main.ts 文件
// main.ts
import {createApp} from 'vue'
import App from './App.vue'
import router from './router'
import './assets/main.css'
import {renderWithQiankun, qiankunWindow} from 'vite-plugin-qiankun/dist/helper'
let instance: any = null
function render(props: any = {}) {
const { container } = props
instance = createApp(App)
instance.use(router)
instance?.mount(container ? container.querySelector('#app') : '#app')
console.log('开始加载相关内容')
}
/*
* bootstrap :
* 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
* 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
* mount :
* 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
* unmount :
* 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
* update :
* 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
* */
renderWithQiankun({
mount(props: any) {
// 应用每次进入都会调用 mount 方法,所以我们在这里初始化一些内容
render(props)
},
bootstrap() {
console.log('微应用初始化的时候调用一次')
},
update() {
console.log('update')
},
unmount(props: any) {
console.log('unmount:应用每次 切出/卸载 会调用的方法', props)
instance.unmount()
instance._container.innerHTML = ''
instance = null
}
})
/*
* 通过 qiankunWindow.__POWERED_BY_QIANKUN__ 判断是不是 qiankun 渲染的,如果不是 qiankun 渲染的,需要调用以下 render 方法来初始化一些内容
* */
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
console.log('并不是qiankun渲染')
render()
}
打包配置修改,需要配置微应用的名字与端口号,使其与主应用配置的微应用保持一致
// vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import qiankun from 'vite-plugin-qiankun'
export default defineConfig({
plugins: [
vue(),
qiankun('vue-app', { // 微应用名字,与主应用注册的微应用名字保持一致
useDevMode: true
})
],
server: {
port: 5174 // 微应用端口号,与主应用注册的微应用保持一致
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
Vue3 不使用 Vite
这里我们就不需要安装任何插件了,只需在微应用的入口文件导出 bootstrap
、mount
、unmount
三个生命周期钩子,以供主应用在适当的时机调用就可以了。
同样还是修改 main.ts 文件,这里的思路和 Vue3 + Vite 的是一样的,只是写法略微不同而已
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
let app = null;
function render(props = {}) {
const { container } = props;
app = createApp(App);
app
.use(store)
.use(router)
.mount(container ? container.querySelector("#app") : "#app");
}
// 独立运行时
// 这里和 Vue3 + Vite 不太一样,是通过 window.__POWERED_BY_QIANKUN__ 来判断是否是 qiankun 渲染的
if (!window.__POWERED_BY_QIANKUN__) {
console.log('独立运行')
render();
}
export async function bootstrap() {
console.log('微应用初始化的时候调用一次');
}
export async function mount(props) {
console.log("mount", props);
render(props);
}
export async function unmount() {
app.unmount();
}
打包配置修改,需要配置微应用的名字与端口号,使其与主应用配置的保持一致
// vue.config.ts
const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package');
module.exports = defineConfig({
transpileDependencies: true,
publicPath: "http://localhost:8080/",
devServer: {
port: 8080, // 微应用端口号,与主应用注册的微应用保持一致
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
},
},
})
Vue2
Vue2 和 Vue3 中不使用 Vite 是一样的,唯一的区别就是 Vue2 和 Vue3 的语法不太一样,直接上代码
// main.js
import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './App.vue';
import routes from './router';
import store from './store';
Vue.config.productionTip = false;
let router = null;
let instance = null;
function render(props = {}) {
const { container } = props;
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');
}
// 独立运行时
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 { defineConfig } = require('@vue/cli-service')
const { name } = require('./package');
module.exports = defineConfig({
transpileDependencies: true,
publicPath: "http://localhost:8080/",
devServer: {
port: 8080,
headers: {
'Access-Control-Allow-Origin': '*',
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
},
},
})
这里只介绍了子应用为 Vue 的情况,当然我们也可以创建一个 React 或者 Angular 的子应用,其配置方法 qiankun 中都有示例
网友评论