使用qiankun搭建微前端遇到的问题

一、为什么要采用微前端

        在公司里面,一般团队都是按照业务去划分的,在没有微前端的时候,如果几个团队维护一个项目肯定会遇到一些冲突,比如合并代码的冲突,上线时间的冲突等。应用了微前端之后,就可以将项目根据业务模块拆分成几个小的模块,每个模块都由不同的团队去维护,单独开发,单独部署上线,这样团队直接就能实现自治,减少甚至不会出现和其他团队冲突的情况。

        如果我们的微前端系统中需要新增一个业务模块时,只需要单独的新建一个项目,至于项目采用什么技术栈,完全可以由团队自己去定义,即使和其他模块用的不同的技术栈也不会有任何的问题。

二、qiankun 

        qiankun是阿里开源的一个微前端的框架,在阿里内部已经经过一批线上应用的充分检验及打磨了,所以可以放心使用。

qiankun的优势是什么?

  • 基于single-spa封装的,提供了更加开箱即用的API

  • 技术栈无关,任意技术栈的应用均可使用/接入,不论是 React/Vue/Angular/JQuery 还是其他等框架。

  • HTML Entry的方式接入,像使用iframe一样简单

  • 实现了single-spa不具备的样式隔离和js隔离

  • 资源预加载,在浏览器空闲时间预加载未打开的微应用资源,加速微应用打开速度。

  • 三、接下来 就使用qiankun搭建一个微前端项目

    首先再自己已有的一个 后台管理项目(React项目)中引入qiankun  指令`npm i qiankun -S`。

    // 在src/index.tsx中增加如下代码

    import { start, registerMicroApps } from 'qiankun';

    // 1. 要加载的子应用列表

const apps = [
  {
    name: "sub-xxx", // 子应用的名称
    entry: '//localhost:8080', // 默认会加载这个路径下的html,解析里面的js
    activeRule: "/sub-react", // 匹配的路由
    container: "#sub-app" // 加载的容器
  },
]

// 2. 注册子应用
registerMicroApps(apps, {
  beforeLoad: [async app => console.log('before load', app.name)],
  beforeMount: [async app => console.log('before mount', app.name)],
  afterMount: [async app => console.log('after mount', app.name)],
})

start() // 3. 启动微服务

当微应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑。 所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。

  • registerMicroApps(apps, lifeCycles?)

    注册所有子应用,qiankun会根据activeRule去匹配对应的子应用并加载

  • start(options?)

    启动 qiankun,可以进行预加载和沙箱设置

        至此基座就改造完成。

四、接下来改造vue子应用

1、安装vite-plugin-qiankun依赖包

npm i vite-plugin-qiankun # yarn add vite-plugin-qiankun

2、修改vite.config.js

import qiankun from 'vite-plugin-qiankun';

defineConfig({
    base: '/sub-vue', // 和基座中配置的activeRule一致
    server: {
      port: 3002,
      cors: true,
      origin: 'http://localhost:3002'
    },
    plugins: [
      vue(),
      qiankun('sub-vue', { // 配置qiankun插件
        useDevMode: true
      })
    ]
})

3、修改main.ts

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';

let app: any;
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  createApp(App).mount('#app');
} else {
  renderWithQiankun({
    // 子应用挂载
    mount(props) {
      app = createApp(App);
      app.mount(props.container.querySelector('#app'));
    },
    // 只有子应用第一次加载会触发
    bootstrap() {
      console.log('vue app bootstrap');
    },
    // 更新
    update() {
      console.log('vue app update');
    },
    // 卸载
    unmount() {
      console.log('vue app unmount');
      app?.unmount();
    }
  });
}

到这里,我们已经完成了应用的加载,在React项目中引入了Vue的子应用。 同理要去改造其他项目的话,也是同理。

五、补充内容

1、对于样式隔离

qiankun实现了各个子应用之间的样式隔离,但是基座和子应用之间的样式隔离没有实现,所以基座和子应用之前的样式还会有冲突和覆盖的情况

解决方法:

  • 每个应用的样式使用固定的格式

  • 通过css-module的方式给每个应用自动加上前缀

2、子应用之前的跳转

  • 主应用和微应用都是 hash 模式,主应用根据 hash 来判断微应用,则不用考虑这个问题。

history模式下微应用之间的跳转,或者微应用跳主应用页面,直接使用微应用的路由实例是不行的,原因是微应用的路由实例跳转都基于路由的 base。有两种办法可以跳转:

  1. history.pushState()

  2. 将主应用的路由实例通过 props 传给微应用,微应用这个路由实例跳转。

具体方案:在基座中复写并监听history.pushState()方法并做相应的跳转逻辑

// 重写函数
const _wr = function (type: string) {
  const orig = (window as any).history[type]
  return function () {
    const rv = orig.apply(this, arguments)
    const e: any = new Event(type)
    e.arguments = arguments
    window.dispatchEvent(e)
    return rv
  }
}

window.history.pushState = _wr('pushState')

// 在这个函数中做跳转后的逻辑
const bindHistory = () => {
  const currentPath = window.location.pathname;
  setSelectedPath(
  	routes.find(item => currentPath.includes(item.key))?.key || ''
  )
}

// 绑定事件
window.addEventListener('pushState', bindHistory)

2、全局状态管理

一般来说,各个子应用是通过业务来划分的,不同业务线应该降低耦合度,尽量去避免通信,但是如果涉及到一些公共的状态或者操作,qiankun也是支持的。

qinkun提供了一个全局的GlobalState来共享数据,基座初始化之后,子应用可以监听到这个数据的变化,也能提交这个数据。

基座:

// 基座初始化
import { initGlobalState } from 'qiankun';
const actions = initGlobalState(state);
// 主项目项目监听和修改
actions.onGlobalStateChange((state, prev) => {
  // state: 变更后的状态; prev 变更前的状态
  console.log(state, prev);
});
actions.setGlobalState(state);

子应用:

// 子项目监听和修改
export function mount(props) {
  props.onGlobalStateChange((state, prev) => {
    // state: 变更后的状态; prev 变更前的状态
    console.log(state, prev);
  });
  props.setGlobalState(state);
}

  • 21
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值