首页 > 其他分享 >qiankun实例化使用

qiankun实例化使用

时间:2023-12-19 16:14:04浏览次数:25  
标签:vue actions js qiankun token 实例 应用 使用 router

一、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才可以跳转

标签:vue,actions,js,qiankun,token,实例,应用,使用,router
From: https://www.cnblogs.com/codeOnMar/p/17860009.html

相关文章

  • MegEngine 优化 dataloader 使用体验!data monitor 帮助更好定位性能瓶颈
    业务模型训练中Data部分可能是瓶颈所在在训练业务模型过程中,如果我们发现模型的训练速度不符合预期,往往会下意识地认为网络本身出了问题。但实际上,大多数时候问题发生在模型的数据供给逻辑中。区分一个训练过程的瓶颈到底是在准备数据,还是在网络的计算阶段其实是很简单的。比......
  • 解决安卓设备无法使用authenticator app问题
    今天要说的是微软的authenticatorapp在中国的使用问题,众所周知的是,微软和很多厂商现在都在大力推广passwordless的身份验证方式,认为username和password已经不再安全,属于要慢慢摒弃的旧方法,至于哪些身份验证方式比较安全,微软心目中认定的身份验证的级别可以参考下图可以看到authent......
  • 使用XPath进行网页爬取的Python实现
    XPath是一种用于在XML和HTML文档中进行导航和查询的语言。在网页爬取中,XPath可以帮助我们定位和提取特定的网页元素,从而实现数据的抓取和提取。本文将介绍如何使用Python中的XPath库来进行网页爬取。1.安装依赖库:在使用XPath进行网页爬取之前,我们需要安装相关的依赖库。Python中常......
  • Python中使用del删除列表元素的原理解析
    Python是一种功能强大的编程语言,提供了许多方便的操作列表的方法。其中,使用del关键字可以删除列表中的某个元素。本文将解析Python中使用del删除列表元素的原理,帮助您理解其工作原理和使用方法。1.列表是可变对象:在Python中,列表是一种可变对象,即可以在原地修改的对象。与不可变对象......
  • springboot 记录使用log4j与logback发送日志到syslog服务器
    Linux服务器配置编辑/etc/rsyslog.conf文件。(我的在这里,根据自己系统查看)#取消注释这几行$ModLoadimudp$UDPServerRun514#ProvidesTCPsyslogreception$ModLoadimtcp$InputTCPServerRun514..........#末位追加:local2.info/var/log/login_info.log......
  • Postgresql中自增主键序列的使用以及数据传输时提示:错误:关系"xxx_xx_xx_seq"不存在
    场景Postgresql在Windows中使用pg_dump实现数据库(指定表)的导出与导入:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/135011801上面讲使用pg_dump进行postgresql的导出与导入。如果使用Navicat可以直接连接两个库,则可直接使用数据传输功能。但是在传输某个表......
  • IDE之VS:Visual Studio的简介(包括 VS2013、VS2015、VS2017、VS2019、VS2022)、安装、
    原文链接:https://blog.csdn.net/qq_41185868/article/details/81052119最近开始使用vs2019,应该是最新的版本。之前都是vs2015,感觉19更智能,兼容性更好,速度也更快。详细了解下这几个版本。1、简介:MicrosoftVisualStudio(简称VS)是美国微软公司的开发工具包系列产品,功能完备的I......
  • qt打开项目缺少ui_文件,使用手动生成(转)
    打开项目看到,缺少ui_myMainWindow.h文件,它是和myMainWindow.ui相对应的,所以我们需要手动生成对应的ui_文件。步骤如下:使用uic.exe来生成,如果在系统变量Path中设置了qt的bin目录,那么就可以直接使用uic.exe。使用方法是:在myMainWindow.ui所在文件夹的空白处点击右键,选择【在终端中......
  • 如何在 ASP.NET Core 中使用 Route 特性
    ASP.NETCore中的Route中间件的职责在于将request匹配到各自Route处理程序上,Route分两种:基于约定和基本特性模式。基于约定模式的Route采用集中化的方式,而基于特性的方式允许你在Action或者Controller上单独定义,到底采用哪一种可以基于你自己的应用场景,本篇就......
  • 【转载】Springboot2.x 使用 Redis
    参考https://blog.csdn.net/weixin_43749805/article/details/131399516https://juejin.cn/post/7076244567569203208https://blog.csdn.net/oJingZhiYuan12/article/details/126386904注意classjava.lang.Integercannotbecasttoclasscom.xiaqiuchu.demo.entity.S......