乾坤-微前端实践

17,332 阅读4分钟

什么是微前端?

要回答这个问题,需要从 why, how, what 三个方面来讲

why:

随着公司业务的膨胀,处理业务的系统数量也跟着膨胀,运营人员处理一单业务需要在各个系统之间来回穿梭。 为了使运营人员在一个系统里可以完成所有操作,技术人员必须给出解决方案。

how:

其实 iframe 在 “微前端” 这个概念被喊出来之前一直是整合系统的利器,但它有些不理想的地方具体原因戳这里 why not iframe,现阶段的前端开发必须找到一种替代方案,优雅的解决掉这些问题。

what:

为了弥补 iframe 方案的不足,让前端开发更方便的把多个业务系统整合到一起,微前端被提出并实现,目前业界使用的方案基本都是基于蚂蚁金服的 qiankun [乾坤], 有天下大一统的味道.

实践前准备

概念: 父应用,子应用

使用 iframe 整合系统时,假设我们有系统 A, 当我们想把系统 B 引入 A 系统时,只需要 B 系统提供一个 url 给 A 系统引用即可,这里我们把 A 系统叫做父应用,把 B 系统叫做子应用。同样的,微前端也延续了这个概念,微前端在使用起来基本和使用 iframe 一样平滑。

乾坤架构图

如图所示,乾坤整合的主要是基于当前主流前端框架 vue, react, agular 实现的系统,jquery 应用支持相地较弱(主要因为大多是多页应用)

整合乾坤

为方便,我们直接使用乾坤提供的 Demo, 请自行 clone 代码库 乾坤demo github

所涉及的项目

父应用
  qiankun-base

三个子应用
  qiankun-react
  qiankun-vue
  qiankun-jq

1. 配置 qiankun-base 父应用

1.1 在 main.js 里引入乾坤,配置要被引入的子应用列表,并启动乾坤

// qiankun-base/src/main.js
// 略去不相关代码
import {registerMicroApps, start } from 'qiankun';
// 定义要整合的微应用列表
const apps = [
  {
    name: 'vueApp', // 应用的名字
    entry: 'http://localhost:10000/', // 默认加载这个html,解析里面的js动态的执行(子应用必须支持跨域,内部使用的是 fetch)
    container: '#vue', // 要渲染到的容器名id
    activeRule: '/vue' // 通过哪一个路由来激活
  },
  {
    name: 'reactApp',
    entry: 'http://localhost:20000/',
    container: '#react',
    activeRule: '/react'
  },
  {
    name: 'jqApp',
    entry: 'http://localhost:5501/qiankun/qiankun-jq/',
    container: '#jq',
    activeRule: '/jq',
    props: { a: 1 } // 父应用向子应用传递参数
  }
];

// 注册应用
registerMicroApps(apps);
// 开启应用
start();

// 正常挂载 vue
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

1.2 在 App.vue 里预留 dom 节点,等待子应用被加载并插入

<!-- qiankun-base/src/App.vue -->
<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>

2. 分别在三个子应用里做处理,准备被 qiankun-base 父应用引用

根据 qiankun 的协议需要导出 bootstrap/mount/unmount 三个引用生命周期钩子函数用于父应用加载子应用时执行,另外父应用会在 window 上添加 POWERED_BY_QIANKUN 属性用于子应用区分当前是否被父应用加载,还是单独加载。所有子应用就围绕 钩子函数 和 属性 做相应配置, 各技术技术栈下处理方式一致

2.1 配置 vue 子应用

// qiankun-vue/src/main.js
// 略去不相关代码
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();
};
// 配置打包 vue.config.js
module.exports = {
  devServer: {
    port: 10000,
    headers:{
      'Access-Control-Allow-Origin': '*' // 允许跨域
    }
  },
  configureWebpack: {
    output: {
      library: 'vueApp',
      libraryTarget: 'umd'
    }
  }
};

2.2 配置 react 子应用

// qiankun-react/src/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'));
}
// 配置启动 config-overrides.js
module.exports = {
  webpack:(config)=>{
    config.output.library = 'reactApp';
    config.output.libraryTarget = 'umd';
    config.output.publicPath = 'http://localhost:20000/';
    return config;
  },
  devServer:(configFunction)=>{
    return function (proxy,allowedHost){
      const config = configFunction(proxy,allowedHost);
      config.headers = {
        "Access-Control-Allow-Origin":'*'
      }
      return config
    }
  }
}

2.3 关于 webpack 的 config 文件说明

注意:config 文件主要改动有两个地方,vue 和 react 子应用一样

1:"Access-Control-Allow-Origin":'*' 作用:由于子应用被父应用 fetch 加载,需要允许跨域

2:output.libraryTarget: 'umd'; 作用:使用 umd 让被webpack打包出来的文件在加载时兼容性更强

2.4 配置 jquery 子应用

注意,微应用一般整合的项目技术栈是基于 vue/react/angular 这种单页应用,而jquery应用一般是多页应用, 这里整合进来的只是一个页面,如果有多个页面的jquery应用不太适合使用乾坤整合,直接用 iframe 引入比较合适

// qiankun-jq/js/example.js
const render = ($) => {
  $('#example').html("Hello, render with jQuery");
  return Promise.resolve();
}
(global => {
  global['purehtml'] = {
    bootstrap: () => {
      console.log('purehtml bootstrap');
      return Promise.resolve();
    },
    mount: (prop) => {
      console.log('purehtml mount', prop);
      return render($);
    },
    unmount: () => {
      console.log('purehtml unmount');
      return Promise.resolve();
    },
  };
})(window);

if (!window.__POWERED_BY_QIANKUN__) { // 如果是独立运行,则手动调用渲染
  render($);
}

3. 启动

分别进入 qiankun-base, qiankun-vue, qiankun-react 启动应用,在父应用里查看即可

注意,jquery 子应用如果想启动需要把它丢到 nginx 服务下

参考:

qiankun 官网

qiankun 作者分享