qiankun框架的使用

226 阅读4分钟

qiankun框架的使用

1、主应用

$ yarn add qiankun # 或者 npm i qiankun -S
1.1 在主应用中注册微应用
import { registerMicroApps } from 'qiankun'
const subArr=[
  {
    name: 'react app', // app name registered
    entry: '//localhost:7100',
    container: '#yourContainer',
    activeRule: '/yourActiveRule',
  },
  {
    name: 'vue app',
    entry: { scripts: ['//localhost:7100/main.js'] },
    container: '#yourContainer2',
    activeRule: '/yourActiveRule2',
  },
  {
    name: 'financial',
    entry: '//localhost:8848/financial-center',
    container: '#yourContainer2',
    activeRule: '/financial'.replace(//(\w+)/, '/m-$1')
  },
]
export function registerApps() {
  window.jQuery = undefined
  try {
    registerMicroApps(subArr, {
      beforeLoad: [
        app => {}
      ],
      beforeMount: [
        app => {}
      ],
      afterMount: [
        app => {}
      ],
      beforeUnmount: [
        app => {}
      ],
      afterUnmount: [
        app => {}
      ]
    })
  } catch (err) {
    console.log('registerApps::', err)
  }
}
​
​
​

说明: ~~~~~ ~~~~~ ~~~~~ ~~~~~ ~~~~

subArr: 表示关于子应用的路由配置:

  • name: 必选,微应用的名称,微应用之间必须确保唯一;

  • entry: - string | { scripts?: string[]; styles?: string[]; html?: string } - 必选,微应用的入口。

    • 配置为字符串时,表示微应用的访问地址,例如 https://qiankun.umijs.org/guide/
  • 配置为对象时,html 的值是微应用的 html 内容字符串,而不是微应用的访问地址。微应用的 publicPath 将会被设置为 /

  • container - string | HTMLElement - 必选,微应用的容器节点的选择器或者 Element 实例。

  • activeRule - string | (location: Location) => boolean | Array boolean>- 必选,微应用的激活规则。

    • 支持直接配置字符串或字符串数组,如 activeRule: '/app1'activeRule: ['/app1', '/app2'],当配置为字符串时会直接跟 url 中的路径部分做前缀匹配,匹配成功表明当前应用会被激活。

    • 支持配置一个 active function 函数或一组 active function。函数会传入当前 location 作为参数,函数返回 true 时表明当前微应用会被激活。如 location => location.pathname.startsWith('/app1')

      注意1:微应用建议使用 history 模式的路由,需要设置路由 base,值和它的 activeRule 是一样的。

      注意2:而且微应用的路由地址和主应用activeRule规则返回的一定不要一样,防止刷新主应用只展示微应用的内容。

      注意3:当浏览器 url 发生变化时,会自动检查每一个微应用注册的 activeRule 规则,符合规则的应用将会被自动激活。

主应用声明周期:

  • beforeLoad - Lifecycle | Array - 可选
  • beforeMount - Lifecycle | Array - 可选
  • afterMount - Lifecycle | Array - 可选
  • beforeUnmount - Lifecycle | Array - 可选
  • afterUnmount - Lifecycle | Array - 可选

1.2 主应用的启动在app.vue文件中

import {
  start,
  initGlobalState,
  addGlobalUncaughtErrorHandler,
  removeGlobalUncaughtErrorHandler
} from 'qiankun'export default {
methods: {
    handleError(event) {
      console.error('qiankun error ->', event)
      if (
        (event.error && event?.error?.stack) ||
        (event.reason && event.reason?.message)
      ) {
        const errmsg = event?.error?.stack || event?.reason?.message
        if (
          (errmsg.includes("Unexpected token '<'") &&
            (errmsg.includes('evalCode') || errmsg.includes('eval'))) ||
          errmsg.includes('fetch dynamically imported module')
        ) {
          console.log("[qiankun] ignore fetch error");
        }
      }
    }
  },
  created() {
    const { onGlobalStateChange,setGlobalState } = initGlobalState(
      this.qiankunState
    )
    // 主应用监听变化 主应用可以通过setGlobalState(value)的方法 可以在onGlobalStateChange监控到
    onGlobalStateChange((value, prev) => {
      console.info('[onGlobalStateChange - master]:', value, prev)
    })
  },
  mounted() {
    // console.log('loadingIsShow111', this.loadingIsShow)
    if (!window.qiankunStarted) {
      window.qiankunStarted = true
      registerApps() // 上述方法请自行引入
      start({
        prefetch: false, // 预加载
        sandbox: {
          experimentalStyleIsolation: false // 样式隔离
        },
        excludeAssetFilter: assetUrl => {
          // 指定部分特殊的动态加载的微应用资源(css/js) 不被 qiankun 劫持处理
          // 自定义白名单链接
          const whiteList = [
            'javascripts/jquery-1.7.2.min.js',
          ]
​
          if (whiteList.find(item => assetUrl.indexOf(item) > -1)) {
            console.log('output->assetUrl', assetUrl)
            return true
          }
        }
      })
​
      // 添加全局的异常处理
      addGlobalUncaughtErrorHandler(this.handleError)
    }
  },
  beforeDestroy() {
    removeGlobalUncaughtErrorHandler(this.handleError)
  }
}

1.3主应的监控和子应用的传递更改


官方案例:

主应用:

import { initGlobalState, MicroAppStateActions } from 'qiankun';
// 初始化 state
const actions: MicroAppStateActions = initGlobalState(state);
actions.onGlobalStateChange((state, prev) => {  // state: 变更后的状态; prev 变更前的状态       console.log(state, prev);
});
actions.setGlobalState(state);actions.offGlobalStateChange();

微应用:

// 从生命周期 mount 中获取通信方法,使用方式和 master 一致
export function mount(props) { 
    props.onGlobalStateChange((state, prev) => {    // state: 变更后的状态; prev 变更前的状态        console.log(state, prev); 
   });
props.setGlobalState(state);}

2、微应用

2.1、微应用以webpack vue2.x版本(不用再单独安装乾坤框架)main.js

let instance = null
// 保存原有document.body.appendChild方法
let originFn = document.body.appendChild.bind(document.body)
function render(props = {}) {
  const { container } = props
  if (container) {
    // 重写appendChild方法
    document.body.appendChild = dom => {
      // 根据标记,来区分是否用新的挂载方式
      if (dom?.className?.indexOf('goods-') > -1) {
        container.querySelector('#goods-center').appendChild(dom)
      } else {
        originFn(dom)
      }
    }
  }
  !router && createRouter()
  instance = new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount(
    container ? container.querySelector('#app') : '#app'
  )
}
​
// 区分是否是qiankun环境
if (!window.__POWERED_BY_QIANKUN__) {
  render()
}
​
export async function bootstrap() {
  console.log('app bootstraped')
}
export async function mount(props) {
  console.log('props from main framework', props)
  render(props)
}
export async function unmount() {
  instance.$destroy()
  instance.$el.innerHTML = ''
  instance = null
  document.body.appendChild = originFn
}
​

区分qiankun环境:window.POWERED_BY_QIANKUN

微应用生命周期:

bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。

通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。

2.2 vue3+ vite微应用 main.js

先下载依赖

pnpm install vite-plugin-qiankun
import App from './App.vue'
import {
  qiankunWindow,
  renderWithQiankun,
} from 'vite-plugin-qiankun/dist/helper'
let app = null
const render = async props => {
  app = createApp(App)
  const { container } = props
    app.mount(
    container instanceof Element
      ? container.querySelector('#markting')
      : document.getElementById('markting'),
  )
}
let qiankunProps = null
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
  render({})
} else {
  renderWithQiankun({
    async mount(props) {
      await render(props)
      props.onGlobalStateChange((state, prev) => {
        // state: 变更后的状态; prev 变更前的状态
        console.log(state, prev)
      })
      qiankunProps = props
    },
    bootstrap() {
      console.log('--bootstrap')
    },
    update() {
      console.log('--update')
    },
    unmount() {
      if (!app) return
      app.unmount()
      if (app?._container?.innerHTML) {
        app._container.innerHTML = ''
      }
      if (app?.$el?.innerHTML) {
        app.$el.innerHTML = ''
      }
      app = null
    },
  })
}
// 监听
export function qiankunEmit(state) {
  state && qiankunProps?.setGlobalState(state)
}

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

微应用分为有 webpack 构建和无 webpack 构建项目,有 webpack 的微应用(主要是指 Vue、React、Angular)需要做的事情有:

  1. 新增 public-path.js 文件,用于修改运行时的 publicPath

    注意:运行时的 publicPath和构建时的 publicPath是不同的,两者不能等价替代。

   在 src 目录新增 public-path.js:
   
   if (window.__POWERED_BY_QIANKUN__) {
     __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
   }
  1. 微应用建议使用 history 模式的路由,需要设置路由 base,值和它的 activeRule 是一样的。
  2. 在入口文件最顶部引入 public-path.js,修改并导出三个生命周期函数。
  3. 修改 webpack 打包,允许开发环境跨域和 umd 打包。

主要的修改就是以上四个,可能会根据项目的不同情况而改变。例如,你的项目是 index.html 和其他的所有文件分开部署的,说明你们已经将构建时的 publicPath 设置为了完整路径,则不用修改运行时的 publicPath (第一步操作可省)。

webpack 构建的微应用直接将 lifecycles 挂载到 window 上即可(vite 可以使用上面得插件)。