一、qiankun使用场景

  1. 简介:qiankun是在single-spa的基础上实现的,可以保证各个项目独立使用,也可以集成使用。各系统之间不受技术栈的限制,集成使用也能保证各样式和全局变量的隔离。

      模块的插拔式使用,当公司项目集是一个大系统下包含多个子系统或者模块时,可以采用这种方式动态部署各个系统。

      亦或者是老项目技术升级和重构,可以通过qiankun按模块进行改造,避免对整个系统产生较大的影响。

      功能和iframe类似,但是由于iframe数据通信难度较大,且有安全和SEO的问题,所以iframe使用体验不佳。

  2. 原理逻辑:

    a. 需要在各个子应用的基础上新增一个主应用,通过主应用监听路由变化。

    b. 当有路由切换时就会触发上述监听函数从而去匹配在主应用中注册的各个子应用路径(activeRule)是否匹配。

    c. 匹配到子应用后就会加载子应用的资源到对应的容器当中去。

二、实现样例

  本样例使用的是Node 16的版本,主应用采用Vue3框架,两个子应用分别使用Vue2和Vue3框架。qiankun版本是2.10.16。

  1. 搭建主应用,利用脚手架创建一个qiankun-main的主应用,同时安装qiankun组件(qiankun只需要在主应用安装,子应用不需要),其中代码中标注重点的内容是配置qiankun的关键步骤

     1.1 打开vue.config.js文件,添加跨域处理,避免跳转时出现跨域问题

const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 8085,
headers: { // 重点1: 允许跨域访问子应用页面
'Access-Control-Allow-Origin': '*',
}
}
})

vue.config.js

     1.2 主应用中设置子应用接收容器

<template>
<div class="app">
<p><router-link to="/">点击跳转到父页面</router-link></p>
<button @click="login">登陆</button>
<p><router-link to="/vue3">跳转到Vue3子应用</router-link></p>
<p><router-link to="/vue2">跳转到Vue2子应用</router-link></p>
<router-view />
<div id="vue3"></div> <!-- 重点2:子应用容器 id -->
<div id="vue2"></div> <!-- 重点2:子应用容器 id -->
</div>
</template> <script>
import actions from '@/shared/actions'; export default {
name: 'App',
components: {
},
mounted() {
actions.onGlobalStateChange((state, prevState) => {
// state: 变更后的状态; prevState: 变更前的状态
console.log('主应用观察者:token值改为', prevState.token);
console.log("主应用观察者:登录状态发生改变,改变后的 token 的值为 ", state.token);
});
},
methods: {
login() {
console.log('进入登陆事件');
setTimeout(() => {
const token = 'token_' + Math.floor(Math.random() * 100000);
//登陆后随机生成token并设置
actions.setGlobalState({ token });
this.$router.push("/vue3");
}, 300);
}
}
}
</script> <style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

App.vue

    1.3 在src根目录下新增public-path文件;同时改造路由,设置返回的base地址

if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

public-path.js

import {
createRouter,
createWebHashHistory
} from 'vue-router'
import '../public-path' // 重点3: 引入public-path文件
const router = createRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/vue3' : '/', // 重点4:qiankun进入子应用时,返回true
history: createWebHashHistory(), // 重点5
routes: [{
path: '/',
redirect: '/child'
},
{
path: '/child',
component: () => import('@/components/child')
}
]
})
export default router

router/index.js

    1.4 注册和引入子应用

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { registerMicroApps, start, setDefaultMountApp } from 'qiankun' createApp(App).use(router).mount('#app') registerMicroApps([
{
name: "vue3 app",
entry: "//localhost:8086", // 重点8:对应重点6
container: '#vue3', // 重点9:对应重点2
activeRule: '/#/vue3', // 重点10:对应重点4
props: {
appContent: '我是主应用传给vue的值'
}
},
{
name: "vue2 app",
entry: "//localhost:8087", // 重点8:对应重点6
container: '#vue2', // 重点9:对应重点2
activeRule: '/#/vue2', // 重点10:对应重点4
props: {
appContent: '我是主应用传给Vue2的值'
}
}
])
setDefaultMountApp("/") // 重点11:启动默认的子模块
// 启动
start()

main.js

  2. 搭建子应用1, 同样利用脚手架创建一个qiankun-vue3-child,项目使用Vue3作为基础框架

    2.1 同样在src目录下创建public-path.js文件

if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}

public-path.js

    2.2 改造router/index.js文件, 确认在qiankun模式下的路由基础路径

import {
createRouter,
createWebHashHistory
} from 'vue-router'
import '../public-path' // 重点3: 引入public-path文件
const router = createRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/vue3' : '/', // 重点4:qiankun进入子应用时,返回true
history: createWebHashHistory(), // 重点5
routes: [
]
})
export default router

router/index.js

    2.3 修改入口函数main.js,导入钩子函数

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import actions from './micros/actions' let instance = null function render(props = {}) {
// qiankun模式下实现父子应用之间通信
if (props) {
actions.setActions(props);
} const { container } = props
// 为了避免根id#app与其他DOM冲突,需要限制查找范围
instance = createApp(App).use(router).mount(container ? container.querySelector('#child-app') : '#child-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() {
if (instance) {
console.log(instance, instance.unmount);
// instance.unmount();
instance = null
}
} // createApp(App).use(router).mount('#child-app')

main.js

    2.4 修改打包配置文件vue.config.js,设置服务端口以及打包模式

const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package');
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 8086, // 重点6
headers: { // 重点7:同重点1,允许子应用跨域
'Access-Control-Allow-Origin': '*',
},
},
// 自定义webpack配置
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把子应用打包成 umd 库格式
// jsonpFunction: `webpackJsonp_${name}`,
},
},
})

vue.config.js

  3. 搭建子应用2,步骤与第2步类似,只是使用Vue2作为基础框架

三、功能演示

四、常见问题

  1. 子应用部署在同一个服务器同一个端口的不同路径下如何配置?

    基本和部署在不同服务器的类似,只是将注册子应用的entry的服务器端口号换成某个路径,同时将打包的publicPath改为该路径

// 主应用入口文件中注册子应用
registerMicroApps([
{
name: "vue3_app",
entry: "/entry_vue3", // 对应之前的 //localhost:8086
container: '#vue3',
activeRule: '/#/vue3',
props: {
appContent: '我是主应用传给vue的值'
}
}
]) // 子应用的 router/indexedDB.js
const router = createRouter({
base: window.__POWERED_BY_QIANKUN__ ? '/vue3' : '/entry_vue3', // 设置路由路径
history: createWebHashHistory(),
routes: [
]
}) // 打包文件vue.config.js中添加默认路径
module.exports = defineConfig({
publicPath: devFlag ? '/' : '/entry_vue3',
transpileDependencies: true,
devServer: {
port: 8087, // 重点6
headers: { // 重点7:同重点1,允许子应用跨域
'Access-Control-Allow-Origin': '*',
},
},
// 自定义webpack配置 重点12
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把子应用打包成 umd 库格式
// jsonpFunction: `webpackJsonp_${name}`,
},
},
})

同服务器同端口部署配置

  2. 主子应用之间通信?

    2.1 使用qiankun框架提供的 initGlobalState 实现的,主要有下面三个函数:

      onGlobalStateChange(callback, Immediately)在当前应用监听全局状态变化;

      setGlobalState(state)按照一级属性进行状态设置,微应用只能修改一级属性;

      offGlobalStateChange()移除当前的状态监听,微应用在unmount时默认调用;

    2.2 使用方式,效果可见上面的案列图中对token的打印信息

      a. 主应用的src目录下新增shared/actions.js文件。

import { initGlobalState } from "qiankun";

const initialState = {
token: 'no token'
};
const actions = initGlobalState(initialState); export default actions;

actions.js

      b. 比如在主应用的App.vue中使用并且实现登陆后生成token以及跳转到vue3子应用

import actions from '@/shared/actions';

export default {
name: 'App',
components: {
},
mounted() {
actions.onGlobalStateChange((state, prevState) => {
// state: 变更后的状态; prevState: 变更前的状态
console.log('主应用观察者:token值改为', prevState.token);
console.log("主应用观察者:登录状态发生改变,改变后的 token 的值为 ", state.token);
});
},
methods: {
login() {
console.log('进入登陆事件');
setTimeout(() => {
const token = 'token_' + Math.floor(Math.random() * 100000);
//登陆后随机生成token并设置
actions.setGlobalState({ token });
this.$router.push("/vue3");
}, 300);
}
}
}

App.vue

      c. 子应用中使用时首先在根目录下创建一个micros/actions.js文件

function emptyAction() {
// 确保单独部署时不会报错
console.warn('当前无可执行的Action');
} class Actions {
// 默认设置空Action
actions = {
onGlobalStateChange: emptyAction,
setGlobalState: emptyAction
} // 设置Actions
setActions(actions) {
this.actions = actions;
} // 映射
onGlobalStateChange(...args) {
return this.actions.onGlobalStateChange(...args);
} // 映射
setGlobalState(...args) {
return this.actions.setGlobalState(...args);
}
} const actions = new Actions();
export default actions;

actions.js

      d. 子应用的APP.vue页面中监听主应用中数据的变化以及子应用主动修改数据观察主应用是否能接收到

import actions from '@/micros/actions.js';

export default {
name: 'App',
components: {
},
mounted() {
actions.onGlobalStateChange(state => {
console.log('子应用Vue的观察函数:', state);
}, true)
},
methods: {
changeToken() {
actions.setGlobalState({ token: 'Vue3_' + Math.floor(Math.random() * 100000) })
}
}
}

App.vue

  3. 各个应用之间如何提取一些公共的资源或者模块?

    可以将公共模块提取成一个公共组件发布到npm,然后由各个应用按需安装。

  4. 各个系统如何做到只登陆一次?

    可以参考单点登陆实现,简单逻辑就是比如:

    a. 有一个地址sso.com做为控制中心,然后a.com、b.com为子模块。

    b. 当访问a.com时无权限时路由会携带参数“a.com”自动跳转到登陆页面,输入用户名密码信息后,经过sso.com验证通过生成ticket并返回给页面同时跳转到a.com并下发ticket。

    c. a.com请求获取到ticket后访问sso.com的服务器进行验证是否有效,有效则允许登陆,这样就完成了一次登陆。

    d. 如果在已登录的状态下跳转到b.com,则省略第二步的登陆验证直接将ticket携带到b.com,然后再访问sso.com进行有消息验证。

注意:主应用注册的activeRule为/vue3时跳转到子应用不生效可能是因为浏览器路由跳转时自动加上/#/,所以在activeRule也需要修改为/#/vue3才可以跳转

qiankun 微前端实例化使用的更多相关文章

  1. 「微前端实践」使用Vue+qiankun微前端方案重构老项目的本地验证

    10月份换了新的工作,参与完一个月的需求迭代后,接到了项目重构的任务.简单来说,需要在短时间内提出方案设想,同时进行本地验证,最终需要拿出一套技术替换方案来.于是,埋头苦干了一个月,总算干了点成绩出来 ...

  2. 基于微前端qiankun的多页签缓存方案实践

    作者:vivo 互联网前端团队- Tang Xiao 本文梳理了基于阿里开源微前端框架qiankun,实现多页签及子应用缓存的方案,同时还类比了多个不同方案之间的区别及优劣势,为使用微前端进行多页签开 ...

  3. vue-qiankun公司微前端项稳定目落地后的总结(附github仓库demo,将会持续更新)

    ️本文为博客园社区首发文章,未获授权禁止转载 大家好,我是aehyok,一个住在深圳城市的佛系码农‍♀️,如果你喜欢我的文章,可以通过点赞帮我聚集灵力️. 个人github仓库地址: https:gi ...

  4. 微前端框架 之 qiankun 从入门到源码分析

    封面 简介 从 single-spa 的缺陷讲起 -> qiankun 是如何从框架层面解决 single-spa 存在的问题 -> qiankun 源码解读,带你全方位刨析 qianku ...

  5. 【微前端】微前端最终章-qiankun指南以及微前端整体探索

    序 这才2月中旬,广州就已经渐渐地进入了夏季,--夏天总是让人焦虑的.过年闲暇时间写下了微前端这系列的终章,欢迎拍砖.如果你习惯直接上手代码,不妨跳到实践一节,直接上代码教程玩一玩. qiankun原 ...

  6. 微前端框架 qiankun 技术分析

    我们在single-spa 技术分析 基本实现了一个微前端框架需要具备的各种功能,但是又实现的不够彻底,遗留了很多问题需要解决.虽然官方提供了很多样例和最佳实践,但是总显得过于单薄,总给人一种&quo ...

  7. 初步认识微前端(single-spa 和 qiankun)

    初步认识微前端 微前端是什么 现在的前端应用,功能.交互日益复杂,若只由一个团队负责,随着时间的推进,会越来越庞大,愈发难以维护. 微前端这个名词,第一次提出是在2016年底.它将微服务(将单一应用程 ...

  8. 微前端(qiankun)主应用共享React组件

    前言 最近需要重构一个老项目,定的方案用微前端去改造.主应用是老的项目,微应用是新的项目,由于重构时间比较紧张,子应用还需要使用父应用的一些组件.过程中遇到一些问题,记录一下. 方案 我们知道qian ...

  9. 微前端大赏二-singlespa实践

    微前端大赏二-singlespa实践 微前端大赏二-singlespa实践 序 介绍singleSpa singleSpa核心逻辑 搭建环境 vue main react child 生命周期 结论 ...

  10. 微前端 & 微前端实践 & 微前端教程

    微前端 & 微前端实践 & 微前端教程 微前端 micro frontends https://micro-frontends.org/ https://github.com/neul ...

随机推荐

  1. Kernel Memory 入门系列: RAG 简介

    Kernel Memory 入门系列: RAG 简介 开一个新坑,Semantic Kernel系列会在 Release 1.0 之后陆续更新. 当我们有了一定的产品资料或者知识内容之后,自然想着提供 ...

  2. 印能捷添加了加密进程导出的pdf文件不加密(rename的类型未添加)

    解决方法:用procmon监控下图的所有文件事件,查找rename的类型,然后右键SetRenameInfo点击Include,将查出来的类型添加到加密后缀中,如下图是一个无后缀的类型需要添加\NOE ...

  3. 香橙派5plus从ssd启动Ubuntu

    官方接口图 我实际会用到的就几个接口,背面的话就一个M.2固态的位置: 其中WIFI模块的接口应该也可以插2230的固态,不过是pcie2.0的速度,背面的接口则是pcie3.0*4的速度,差距还是挺 ...

  4. Swagger配置类

    Swagger配置类 package com.guoba.servicebase.config; import com.google.common.base.Predicates; import or ...

  5. 【fmjava】 面试题突击训练-Java基础语法篇01

    JDK 和 JRE 有什么区别? JDK:Java Development Kit 的简称, Java 开发工具包, 提供了 Java 的开发环境和运行环境. JRE:Java Runtime Env ...

  6. libGDX游戏开发之NPC敌人事件(六)

    libGDX系列,游戏开发有unity3D巴拉巴拉的,为啥还用java开发?因为我是Java程序员emm-国内用libgdx比较少,多数情况需要去官网和google找资料,相互学习的可以加我联系方式. ...

  7. Git commit emoji 对照表

    emoji emoji代码 commit说明 (调色板) :art: 改进代码结构/代码格式 ️ (闪电) :zap: 提升性能 (赛马) :racehorse: 提升性能 (火焰) :fire: 移 ...

  8. cookie和session的一些疑惑以及ai解答

    我: 那么当浏览器关闭的时候,当再次访问这个地址的时候,为什么之前设置的cookie没有被删除掉?而且按照你说的这次可能会生成一个新的sessionID,那么cookie里面的其他数据,它是如何获取上 ...

  9. Docker、pre-commit 导致的 git commit 报错:找不到 python 3.8

    到这个问题的原因可能有很多,这里只是记录下针对我遇到这这跟题的原因及解决方法 问题描述 执行 git commit 命令,报错 /usr/bin/env: 'python3.8': No such f ...

  10. GaussDB技术解读:应用无损透明(ALT)

    本文分享自华为云社区<DTCC 2023专家解读丨GaussDB技术解读系列之应用无损透明(ALT)>,作者: GaussDB 数据库. 近日,在第14届中国数据库技术大会(DTCC 20 ...