乾坤微服务的使用

前言:

        在这里整理下用乾坤来开发微服务的一些资料。

使用好处:

        使用乾坤可以实现什么效果呢?众所周知,前端的框架五花八门,react/vue/angular等各领风骚,那么如果我们有需要把不同技术栈的项目整合起来,应该怎么去做呢?如果统一技术栈进行开发,工作量太大,成本也高,那还能怎么做呢?没错,这就是我们乾坤技术出来的由来,可以通过他把不同的项目进行一个融会贯通,让他们可以实现亲密的联系,又能各自发展。

乾坤的官网地址:点我

乾坤的逻辑流程:

如何去使用:

1、安装
yarn add qiankun
npm i qiankun -S 
2、主应用的main.js
// (1)导入乾坤框架
import { registerMicroApps, start } from "qiankun";
// (2)注册乾坤子应用
registerMicroApps([
  {
    name:"sub-application01", //子应用名称
    entry:"//localhost:8001", //子应用入库地址
    container:"#container", //主应用容器
    activeRule:"/sub-application01", //主应用路由匹配规则
    props:{
      token:"sub-application-001"
    } //主应用传递参数
  },
  // {
  //   name:"sub-application02",
  //   entry:"//localhost:8002",
  //   container:"#container",
  //   activeRule:"/sub-application02",
  //   props:{
  //     token:"sub-application-002"
  //   }
  // }
]);

 
//(3)开启乾坤
start();
3、主应用的配置  initGlobalState(state)
  • 参数
  • state - Record<string, any> - 必选
  • 用法定义全局状态,并返回通信方法,建议在主应用使用,微应用通过 props 获取通信方法
import { initGlobalState } from 'qiankun';

// 跨应用共享状态
const initialState = {
  hasCallPhone: false, // 是否拨打电话
  outsidePhone: '', // 外部电话号码
  isLocal: true, // 是否是本地号码
  channelId: '', // 渠道
  leadsId: '',
  hasSendMsg: false, // 是否发送短信
  maSend: {}, // MA的leadsId,channelId
  hasSendEmail: false, // 是否发送邮件
  contactHistory: false, // 是否展示联系历史
  customerId: '', // 联系历史需要的id,
  newDict: false, // 是否新增字典
  addDictId: '', // 传入字典id
  callDetails: false, // 是否展示通话详情
  channelSessionId: '', // 通话详情需要的id
  urgentObj: null, // 获取紧急程度
  socketCallback: null,
  taskList: [],
  isCustomerEdit: false, // 是否可以编辑客户
  trendsLayout: [], // 客户表单
  dynamicFields: [], // 动态字段
  fixedFieldsComponent: [], // 固定字段
  operateType: '', // 操作方式,是新增还是编辑
  callerName: '', // 主叫号人员名称
  calledName: '', // 被叫号人员名称
  roomNumber: '', // csp呼叫房间
  softPhone: {
    curOperate: '', // 呼叫状态
    hasSipConnected: false, // 电话连接状态
    mediaAvailabled: false, // 音频媒体
    webrtcConfig: {}, // 初始化连接webrtc配置
  },
  imPageNoticeInfo: {}, // 内部聊天页面通知相关数据
  iqcPageNoticeInfo: {}, // 内部支持页面通知相关数据
  reconnectCallback: null, // 内部支持断网重连回调
  reconnectImCallback: null, // IM
  callVoiceCallback: null,
  callVoiceInfo: {},
  goConversation: false, // 通讯录跳转
};
const actions = initGlobalState(initialState);

export default actions;
4、主应用中手动加载微应用的方式:
import { loadMicroApp } from 'qiankun';
let leftMicroApp = null;

方法内部:
leftMicroApp = loadMicroApp({
  name: 'crm_core',
  entry: '//localhost:9011',
  container: '#wrapper__right',
  props: {
    path: 'customerTabs',
  },
});

//组件销毁,调用子应用的 unmount方法
destroyed() {
  leftMicroApp.unmount()
},
5、子应用中
1、新建文件:public-path.ts/ public-path. js
/* eslint-disable camelcase */
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
2、main.ts/main.js
import './core/public-path'

// vue3中写法
const __qiankun__ = window.__POWERED_BY_QIANKUN__
__qiankun__ || render()



// vue2中写法
//创建子应用渲染函数
function render(props = {}) {
  const { container } = props;
  router = new VueRouter({
    base: window.__POWERED_BY_QIANKUN__ ? '/app-vue/' : '/',
    mode: 'history',
    routes,
  });
 
  instance = new Vue({
    router,
    render: (h) => h(App),
  }).$mount(container ? container.querySelector('#app') : '#app');
};
 
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
  render();
};
3、打包配置,vue.config.js
configureWebpack: {
    output: {
      library: `${name}-[name]`,
      libraryTarget: 'umd', // 把微应用打包成 umd 库格式
      jsonpFunction: `webpackJsonp_${name}`, 
      filename: 'static/js/[hash:8].bundle.js'
    },
  },
6、微应用不需要额外安装任何其他依赖即可接入 qiankun 主应用。

微应用需要在自己的入口 js (通常就是你配置的 webpack 的 entry js) 导出 bootstrap、mount、unmount 三个生命周期钩子,以供主应用在适当的时机调用。

生命周期钩子封装

/**
 * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
 * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
 */
export async function bootstrap() {
  console.log('react app bootstraped');
}
/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount(props) {
 
}

/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount(props) {
  
}

/**
 * 可选生命周期钩子,仅使用 loadMicroApp 方式加载微应用时生效
 */
export async function update(props) {
  console.log('update props', props);
}

个人项目中用法:

main.ts

import './core/public-path'
import { lifeCycle, render } from './core/life-cycle'

const { bootstrap, mount, unmount } = lifeCycle()
export { bootstrap, mount, unmount }

const __qiankun__ = window.__POWERED_BY_QIANKUN__
__qiankun__ || render()

life-cycle.ts

...
/**
 * 微应用生命周期
 */
const lifeCycle = (): { [key: string]: (props?: qkProps) => Promise<void> } => {
  return {
    async bootstrap(props) {
      console.log(`bootstrap props: ${props}`);
    },
    async mount(props) {
      console.log(`mount props: ${props}`);
      if (props) {
        // 生成动态路由
        const availRoutes = assyAvailRoutes(props.menuLists, 1, "", APP_NAME);
        // 扁平化菜单树
        const flatMenus = flatMenuTree(props.menuLists);
        // 将菜单树、动态路由、扁平菜单树存入全局状态中
        store.commit("user/SET_MENUS", { menuLists: props.menuLists, availRoutes, flatMenus });
        // 将角色列表存入全局状态中
        store.commit("user/SET_ROLES", props.roles);
        store.commit("user/SET_USERINFO", props.userInfo);
        const routes = selfRoutes.concat(availRoutes);
        props.routes = routes;
        store.commit("chat/SET_SINGLE_CONFIG_EVO", []);
        // 如果开启内部聊天语音通话时获取有没有语音聊天权限
        if (matchFuncConfig("INTERNALCHAT_SOFTPHONE_ACTIVE") && store.state.chat.singleConfigEvo.length === 0) {
          getSingleMyConfigs();
        }
        // props.functions.sendOrder({
        //   message: {
        //     type: 'typing',
        //     sendUserId: '',
        //     groupType: ''
        //   }
        // });
        actions.setActions(props);
        actions.setGlobalState({
          socketCallback: (data: any, extraParams: any) => {
            store.commit("chat/SET_SOCKET_MAINAPP_PARAMS", extraParams);
            const { namespace } = extraParams;
            // 接收到父应用分发的消息,进行处理
            if (namespace === "im") {
              if (data.type === spm.ON_PING) {
                imDispatchMessage({ messageType: cmd.SOCKET_PING });
              } else {
                imDispatchMessage({
                  messageType: enumMsg[data.messageType],
                  message: data.message,
                });
              }
            }
            if (namespace === "iqc") {
              if (data.type === spm.ON_PING) {
                iqcDispatchMessage({ messageType: cmd.SOCKET_PING });
              } else {
                iqcDispatchMessage({
                  messageType: enumMsg[data.messageType],
                  message: data.message,
                });
              }
            }
          },
          // 断网重连回调
          reconnectCallback: () => {
            store.commit("internal/SET_RECONNECTED_COUNT");
          },
          // 断网重连回调
          reconnectImCallback: (networkStatus:string) => {
            utilHelper.handleDisconnectOrOnreconnected(networkStatus)
            console.log('##################执行reconnectImCallback',networkStatus);
          },

        });
      }
      await render(props);
    },
    async unmount() {
      // 关闭所有的页面通知实例
      const { pageNoticeInstances = {} } = store.state.chat;
      const instanceKeys = Object.keys(pageNoticeInstances);
      forEach(instanceKeys, (key) => {
        const notifyInstance = pageNoticeInstances[key];
        notifyInstance.close();
      });
      console.log("unmount props");
      instance.unmount();
      instance = null;
      router = null;
    },
    async update(props) {
      console.log(`update props: ${props}`);
    },
  };
};

async function render(props?: qkProps): Promise<void> {
  let basePath = "";
  // 如果是生产环境
  if (process.env.NODE_DEV === "production") {
    // 如果是子应用,使用二级域名前缀,反之使用带internalPortal三级域名
    basePath = __qiankun__ ? `/${APP_NAME}` : `/internalPortal/${APP_KEY}/`;
  } else {
    // 如果非生产环境,并且不是子应用,
    basePath = __qiankun__ ? `/${APP_NAME}` : "/";
  }

  // 初始化固定路由
  let routes = selfRoutes;
  if (__qiankun__) {
    // 如果是微应用,则使用主应用传递的路由集合
    if (props?.routes) routes = props?.routes;
  } else if (store.state.user.accessToken) {
    // 如果没有授权令牌
    // 请求菜单树,非子应用时不控制权限
    const response: AxiosResponse = await axiosSingle(getCompleteTree(), false);
    if (response.data.length > 0) {
      // 获取当前子应用相关的菜单
      let menuLists = response.data[0].children.filter((item: IMenu) =>
        includes(["conversation", "organization"], item.i18n)
      );
      // 递归生成菜单
      menuLists = recurseTree(menuLists, "");
      if (menuLists.length) {
        // 生成动态路由
        const availRoutes = assyAvailRoutes(menuLists, 1, "", APP_NAME);
        // 扁平化菜单树
        const flatMenus = flatMenuTree(menuLists);
        // 将菜单树、动态路由、扁平菜单树存入全局状态中
        store.commit("user/SET_MENUS", { menuLists, availRoutes, flatMenus });
        // 叠加固定路由和动态路由
        // routes = selfRoutes.concat(availRoutes)
        selfRoutes[0].children = availRoutes;
        routes = selfRoutes;
      }
    }
  }
  router = createRouter({
    history: createMemoryHistory(basePath),
    routes,
  });
  instance = createApp(App).use(router).use(store).use(i18n).use(plugin, { imports: [] });

  // 全局注册El组件
  components.forEach((item) => {
    if (item) instance.use(item);
  });
  // 全量导入El图标
  for (const key in Icons) {
    if (Reflect.has(Icons, key)) {
      instance.component(key, Icons[key]);
    }
  }
  // 注册按钮授权指令
  instance.use(authDirective);
  // 注册按钮授权全局方法
  instance.config.globalProperties.$vAuth = function (key: any) {
    return directiveAuth(this, key);
  };
  instance.use(draggable);

  instance.mount(props?.container ? props.container.querySelector("#appInternalChat") : "#appInternalChat");
  // instance.use(VueVirtualScroller);
  // instance.component('DynamicScroller', VueVirtualScroller.DynamicScroller)

  // 前置路由守卫
  router.beforeEach(async (to: any, from: any) => {
    if (!__qiankun__) {
      // 1 如果不是子应用
      if (store.state.user.accessToken) {
        if (!store.state.user.userInfo) {
          const infoConfig = configRequest(`${GlobalConfig.API_HRMS_URL}/employee/current`, httpMethod.GET);
          const response1 = await axiosSingle(infoConfig);
          const userInfo = response1.data;
          store.commit("user/SET_USERINFO", userInfo);
          // 1.1 如果有授权令牌
          if (to.path === "/login") {
            // 1.1.1 如果访问页面为登录页,则跳转到首页
            return "/";
          } else if (to.matched.length) {
            // 1.1.2 如果有匹配的路由,则进行跳转
            return true;
          } else {
            // 1.1.3 如果找不到匹配路由,则跳转到未授权报错页面
            // next({ path: '/403', replace: true })
            return false;
          }
        }
      } else if (to.path === "/login" && to.query.code) {
        // 1.2 如果没有令牌并跳转到登录页,并有授权码
        return true;
      } else {
        // 如果没有令牌并且没有授权码,则跳转到sso进行登录
        signIn();
      }
    } else if (to.matched.length) {
      // 2 如果是子应用,并且有匹配的路由,则进行跳转
      return true;
    } else {
      // 3 如果没有匹配路由,则跳转到未授权报错页面
      // next({ path: '/403', replace: true })
      return false;
    }
  });
}

export { lifeCycle, render };

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/734881.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UFS Power Mode Change 介绍

一. UFS Power Mode Change简介 1.UFS Power Mode指的是Unipro层的Power State, 也可以称为链路(Link)上的Power Mode, 可以通过配置Unipro Attribute, 然后控制切换Unipro Power State, 当前Power Mode Change有两种触发方式&#xff1a; (1) 通过DME Power Mode Change触发…

Tortoise 删除文件

1、右击需要删除的文件&#xff0c;选择Delete 2、提交

遗传算法求解时间窗车辆路径规划问题(附python代码)

摘要 本研究提出了一种基于遗传算法的车辆路径规划&#xff08;VRP&#xff09;问题求解框架&#xff0c;它能够有效地处理一系列复杂约束&#xff0c;包括软时间窗、硬时间窗、行驶距离限制、车辆最大载重量、多个配送中心的协调、特定的配送顺序&#xff0c;以及多种车型的选…

【Python系列】探索 NumPy 中的 mean 函数:计算平均值的利器

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【AI技术】GPT-4o背后的语音技术猜想

前言&#xff1a; 本篇文章全文credit 给到 台大的李宏毅老师&#xff0c;李宏毅老师在机器学习上风趣幽默、深入浅出的讲解&#xff0c;是全宇宙学AI、讲中文学生的福音&#xff0c;强力推荐李宏毅老师的机器学习课程和深度学习 人工智能导论&#xff1b; 李宏毅老师的个人长…

LabVIEW机器视觉在质量控制中的应用

基于LabVIEW的机器视觉系统在质量控制中应用广泛&#xff0c;通过图像采集、处理和分析&#xff0c;自动检测产品缺陷、测量尺寸和识别标记&#xff0c;提高生产效率和产品质量。下面介绍LabVIEW机器视觉系统在质量控制中的实现方法、应用场景及其优势。 项目背景 在现代制造业…

Redis 入门篇

文章目录 Redis简介关系型数据库:非关系型数据库 Redis应用场景Redis下载和安装Redis 数据类型Redis 常用命令字符串 string 操作命令哈希 hash 操作命令列表 list 操作命令集合 set 操作命令有序集合 sorted set 操作命令通用命令 Jedis 快速入门配置依赖建立连接 / 操作 Jedi…

ShareX,屏幕截图、屏幕录制和文件共享,还提供了丰富的高级功能和自定义选项

ShareX是一个免费开源的Windows应用程序&#xff0c;用于屏幕截图、屏幕录制和文件共享。它不仅支持基本的屏幕截图功能&#xff0c;还提供了丰富的高级功能和自定义选项&#xff0c;使其成为提高工作效率和截图体验的利器。以下是ShareX v16.1.0便携版的主要功能和特色&#x…

NeRF从入门到放弃4: NeuRAD-针对自动驾驶场景的优化

NeuRAD: Neural Rendering for Autonomous Driving 非常值得学习的一篇文章&#xff0c;几乎把自动驾驶场景下所有的优化都加上了&#xff0c;并且也开源了。 和Unisim做了对比&#xff0c;指出Unisim使用lidar指导采样的问题是lidar的垂直FOV有限&#xff0c;高处的东西打不…

Vue: Module “vue“ has no exported member xxx

这个问题让我困扰了好一会儿&#xff0c;我询问了 chatgpt 和各种网站社区&#xff0c;尝试了切换依赖的版本&#xff0c;清除缓存等等&#xff0c;依然没有解决 不过算是有心栽花花不开&#xff0c;无心插柳柳成荫&#xff0c;碰巧解决了&#xff0c;也不知道是不是这个原因&a…

java收徒 java辅导 java试用期辅导 java零基础学习

&#x1f497;博主介绍&#xff1a;✌全网粉丝1W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末报名辅导&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;还有大家…

WinMerge v2 (开源的文件比较/合并工具)

前言 WinMerge 是一款运行于Windows系统下的免费开源的文件比较/合并工具&#xff0c;使用它可以非常方便地比较多个文档内容甚至是文件夹与文件夹之间的文件差异。适合程序员或者经常需要撰写文稿的朋友使用。 一、下载地址 下载链接&#xff1a;http://dygod/source 点击搜…

微信小程序-伪类选择器

一.伪类选择器 结构伪类常见书写方式&#xff1a; 第一类&#xff1a;找第几个孩子 1. :first-child 找第一个孩子2. :last-child 找最后一个孩子3. :nth-child()&#xff0c;正着找数字&#xff1a;写数字几就是找第几个孩子&#xff0c;2n或者even:找偶数2n1或者o…

python数据分析案例-信用卡违约预测分析

一、研究背景和意义 信用卡已经成为现代社会中人们日常生活中不可或缺的支付工具&#xff0c;它不仅为消费者提供了便利&#xff0c;还为商家提供了更广泛的销售渠道。然而&#xff0c;随着信用卡的普及和使用量的增加&#xff0c;信用卡违约问题逐渐成为金融机构面临的重要挑…

Java基础的重点知识-03-方法与数组

文章目录 方法数组 方法 定义方法的格式详解 修饰符 返回值类型 方法名(参数列表){//代码省略...return 结果; }修饰符&#xff1a; public static 固定写法返回值类型&#xff1a; 表示方法运行的结果的数据类型&#xff0c;方法执行后将结果返回到调用者参数列表&#xff1…

Pytho字符串的定义与操作

一、字符串的定义 Python 字符串是字符的序列&#xff0c;用于存储文本数据。字符串可以包括字母、数字、符号和空格。在 Python 中&#xff0c;字符串是不可变的&#xff0c;这意味着一旦创建了一个字符串&#xff0c;就不能更改其中的字符。但是&#xff0c;你可以创建新的字…

一文读懂LLM API应用开发基础(万字长文)

前言 Hello&#xff0c;大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者&#xff0c;上一篇文章中我们详细介绍了LLM开发的基本概念&#xff0c;包括LLM的模型、特点能力以及应用&#xff1b;&#x1f632; 在本文中作者将通过&#xff1a…

Flutter ListView详解

文章示例代码 ListView常用构造 ListView 我们可以直接使用ListView 它的实现也是直接返回最简单的列表结构&#xff0c;粗糙没有修饰。 ListView 默认构建 效果 ///默认构建 Widget listViewDefault(List list) { List _list new List(); for (int i 0; i < list.le…

Java学习 - 网络IP协议簇 讲解

IP协议 IP协议全称 Internet Protocol互联网互连协议 IP协议作用 实现数据在网络节点上互相传输 IP协议特点 不面向连接不保证可靠 IP协议数据报结构 组成说明版本目前有IPv4和IPv6两种版本首部长度单位4字节&#xff0c;所以首部长度最大为 15 * 4 60字节区分服务不同…

视觉新纪元:解码LED显示屏的视角、可视角、最佳视角的最终奥秘

在璀璨夺目的LED显示屏世界里&#xff0c;每一个绚烂画面的背后&#xff0c;都离不开三个关键概念&#xff1a;视角、可视角与最佳视角。这些术语不仅是衡量显示效果的重要标尺&#xff0c;也是连接观众与精彩内容的桥梁。让我们一起走进这场视觉盛宴&#xff0c;探索那些让LED…