微前端(qiankun)快速上手

479 阅读2分钟

主应用

1. 安装 qiankun

$ yarn add qiankun # 或者 npm i qiankun -S

qiankun只需要主应用安装呦,微应用(子应用)只需要导出指定的生命周期钩子

2. 在主应用中注册微应用

import { registerMicroApps, start } from 'qiankun';


registerMicroApps([
  {
    // name - string - 必选,微应用的名称,微应用之间必须确保唯一。
    name: 'react app', 
    /**
    * entry
    * `string | { scripts?: string[]; styles?: string[]; html?: string }`
    * 必选,微应用的入口
    * 配置为字符串时,表示微应用的访问地址,例如 `https://qiankun.umijs.org/guide/`
    * 配置为对象时,`html` 的值是微应用的 html 内容字符串,而不是微应用的访问地址。微应用的 `publicPath` 将会被设置为 `/`
    */
    entry: '//localhost:7100',
    /**
    * container
    * `string | HTMLElement`
    * 必选,微应用的容器节点的选择器或者 Element 实例。
    * 如`container: '#root'` 或 `container: document.querySelector('#root')`。
    */
    container: '#yourContainer',
    /**
    * activeRule
    * `string | (location: Location) => boolean 
    * Array<string | (location: Location) => boolean> `
    * 必选,微应用的激活规则
    */
    activeRule: '/yourActiveRule',
  },
  {
    name: 'vue app',
    entry: { scripts: ['//localhost:7100/main.js'] },
    container: '#yourContainer2',
    activeRule: '/yourActiveRule2',
  },
]);

// 启动 qiankun
start();

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

注意container要在主应用预留好坑位,便于微服务(子应用)插入 例如主应用使用vue框架在app.js文件中

<template>
  <div>
    // 主应用模块
    <el-menu :router="true" mode="horizontal">
      <!-- 主应用中也可以放自己的路由 -->
      <el-menu-item index="/">首页</el-menu-item>
      <!-- 引用其他的子应用 -->
      <el-menu-item index="/vue">vue应用</el-menu-item>
      <el-menu-item index="/react">react应用</el-menu-item>
    </el-menu>
    <router-view v-show="$route.name"></router-view>
    
    // 子应用预留坑位
    <div id="vue"></div>
    <div id="react"></div>
  </div>
</template>

如果微应用不是直接跟路由关联的时候,你也可以选择手动加载微应用的方式:

import { loadMicroApp } from 'qiankun';


loadMicroApp({
  name: 'app',
  entry: '//localhost:7100',
  container: '#yourContainer',
});

微应用(子应用)

微应用不需要额外安装任何其他依赖即可接入 qiankun 主应用。

1. 导出相应的生命周期钩子

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

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


/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
export async function mount(props) {
  ReactDOM.render(<App />, props.container ? props.container.querySelector('#root') : document.getElementById('root'));
}


/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
export async function unmount(props) {
  ReactDOM.unmountComponentAtNode(
    props.container ? props.container.querySelector('#root') : document.getElementById('root'),
  );
}


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

举个🌰react示例:

// index.js文件
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

function render(){
  ReactDOM.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>,
    document.getElementById('root')
  );
}
if(!window.__POWERED_BY_QIANKUN__){
  render();
}
export async function bootstrap(){

}
export async function mount() {
  render()
}
export async function unmount(){
  ReactDOM.unmountComponentAtNode( document.getElementById('root'));
}

举个🌰vue示例

// main.js文件
import Vue from 'vue'
import App from './App.vue'
import router from './router'

Vue.config.productionTip = false

let instance = null;
function render(props) {
  // props 组件通信
  instance = new Vue({
    router,
    render: h => h(App)
  }).$mount('#app') // 这里是挂载到自己的HTML中,基座会拿到这个挂载后的HTML,将其插入进去
}

if (!window.__POWERED_BY_QIANKUN__) { // 如果是独立运行,则手动调用渲染
  render();
}
if(window.__POWERED_BY_QIANKUN__){ // 如果是qiankun使用到了,则会动态注入路径
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}

// 根据 qiankun 的协议需要导出 bootstrap/mount/unmount
export async function bootstrap(props) {

};
export async function mount(props) {
  render(props);
};
export async function unmount(props) {
  instance.$destroy();
};

qiankun 基于 single-spa,所以你可以在这里找到更多关于微应用生命周期相关的文档说明。

无 webpack 等构建工具的应用接入方式请见这里

2. 配置微应用的打包工具

除了代码中暴露出相应的生命周期钩子之外,为了让主应用能正确识别微应用暴露出来的一些信息,微应用的打包工具需要增加如下配置:

webpack:

const packageName = require('./package.json').name;


module.exports = {
  output: {
    library: `${packageName}-[name]`,
    libraryTarget: 'umd',
    jsonpFunction: `webpackJsonp_${packageName}`,
  },
  devServer: {
    headers:{
      'Access-Control-Allow-Origin': '*' // 允许跨域
    }
  },
};

相关配置介绍可以查看 webpack 相关文档

qiankun

示例参考(来自 乾坤-微前端实践 分享)