AlienGao's blog AlienGao's blog
首页
  • JavaScript
  • Antd组件
  • 学习笔记

    • 《ES6 教程》笔记
  • 贪心
  • 广度优先/深度优先
  • 位运算
  • 技术文档
  • GitHub技巧
面试
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

AlienGao

首页
  • JavaScript
  • Antd组件
  • 学习笔记

    • 《ES6 教程》笔记
  • 贪心
  • 广度优先/深度优先
  • 位运算
  • 技术文档
  • GitHub技巧
面试
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

    • qiankun微前端应用
      • 主应用配置
      • 子应用配置
  • GitHub技巧

  • 技术
  • 技术文档
AlienGao
2021-08-11

qiankun微前端应用

根据公司项目需要,将老项目的视频监控功能移植到新项目中,正好有机会实践一下qiankun框架。根据官网 (opens new window)介绍,应用qiankun对技术栈没有限制,由于qiankun的Api非常简洁,主应用与子应用进行简单配置,就能完成改造。最后分享一个根据官网搭建的qiankun微应用,实现父应用与子应用的�通信,若是qiankun启动,兄弟应用可以通过修改父应用的全局状态来通信。

项目地址:https://github.com/AlienGao/qiankun-demo (opens new window)

# 主应用配置

main/src/micro-apps.js

import shared from './shared'

const mircoApps = [
  {
    name: 'react-app',
    entry: process.env.REACT_APP_REACT_PATH,
    activeRule: '/app-react',
  },
  {
    name: 'vue-app',
    entry: process.env.REACT_APP_VUE_PATH,
    activeRule: '/app-vue',
  }
]

// console.log(process.env, 'vue_path')

const apps = mircoApps.map(app => {
  return {
    ...app,
    // 子应用容器
    container: '#container',
    props: {
      // 暴露给子应用的方法
      getGlobalState: shared.getGlobalState,
      setGlobalState: shared.setGlobalState
    }
  }
})

export default apps

main/src/shared/index.js

import { initGlobalState } from 'qiankun';

const initialState = { taskList: [] };

const actions = initGlobalState(initialState);

actions.onGlobalStateChange((newState, prev) => {
  // state: 变更后的状态; prev 变更前的状态
  console.log('main change', JSON.stringify(newState), JSON.stringify(prev));

  for (let key in newState) {
    initialState[key] = newState[key]
  }
}, true);

// 定义一个获取state的方法下发到子应用
actions.getGlobalState = (key) => {
  // 有key,表示取globalState下的某个子级对象
  // 无key,表示取全部
  return key ? initialState[key] : initialState
}

// actions.setGlobalState(state);

export default actions;

这两个文件主要配置了子应用的端口,容器和需要用到的方法,通过将setGloablState暴露给子应用,可以做到通过一个子应用修改全局状态再同步给另一个子应用。

最后,在父应用的入口文件注册子应用。

import reportWebVitals from './reportWebVitals';
import { registerMicroApps, start, setDefaultMountApp } from 'qiankun';
import microApps from './micro-apps'

const apps = microApps

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

registerMicroApps(apps, {
  beforeLoad: [app => {
    console.log(`${app.name}-beforeLoad`)
  }],
  beforeMount: [app => {
    console.log(`${app.name}-beforeMount`)
  }],
  afterMount: [app => {
    console.log(`${app.name}-afterMount`)
  }],
  beforeUnmount: [app => {
    console.log(`${app.name}-beforeUnmount`)
  }],
  afterUnmount: [app => {
    console.log(`${app.name}-beforeUnmount`)
  }]
});

setDefaultMountApp('/app-vue')
// 启动 qiankun
start();

效果图 上面为效果图,上方为主应用,下方位微应用容器。

# 子应用配置

子应用首先需要在入口文件处配置三个钩子函数用于qiankun识别生命周期。

vue-app/src/main.js

export async function bootstrap() {
  console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
  console.log('[vue] props from main framework', props);
  globalRegister(store, props)
  render(props);
}
export async function unmount() {
  instance.$destroy();
  instance.$el.innerHTML = '';
  instance = null;
  router = null;
}

其次在Vue子应用mount时期,通过globalRegister函数初始化了全局状态。

vue-app/src/shared/index.js

/**
 * 
 * @param {vuex实例} store 
 * @param {qiankun下发的props} props 
 */
function registerGlobalModule(store, props = {}) {
  if (!store || !store.hasModule) {
    return;
  }

  // 获取初始化的state
  const initState = props.getGlobalState && props.getGlobalState() || {};
  // 将父应用的数据存储到子应用中,命名空间固定为global
  if (!store.hasModule('global')) {
    const globalModule = {
      namespaced: true,
      state: initState,
      actions: {
        // 子应用改变state并通知父应用
        setGlobalState({ commit }, payload) {
          commit('setGlobalState', payload);
          commit('emitGlobalState', payload);
        },
        // 初始化,只用于mount时同步父应用的数据
        initGlobalState({ commit }, payload) {
          commit('setGlobalState', payload);
        },
      },
      mutations: {
        setGlobalState(state, payload) {
          // eslint-disable-next-line
          state = Object.assign(state, payload);
        },
        // 通知父应用
        emitGlobalState(state) {
          if (props.setGlobalState) {
            // console.log(state, 'emitGlobalState')
            props.setGlobalState(state);
          }
        },
      },
    };
    store.registerModule('global', globalModule);
  } else {
    // 每次mount时,都同步一次父应用数据
    store.dispatch('global/initGlobalState', initState);
  }
}

export default registerGlobalModule;

若Vue应用没有global Modal,则根据全局状态创建一个。其中能够通过父应用传递的setGlobalState方法改变父应用状态。

  computed: {
    taskList() {
      return this.$store.state.global.taskList;
    }
  },
methods: {
    onChange(task) {
      this.$store.dispatch('global/setGlobalState', task)
      // this.microSetGlobalState(this.$store.state.global)
        }
  },

Vue应用可以通过计算属性获取最新的父应用状态。其中有两种方式修改父应用状态,一种是通过dispatch 'global/setGlobalState' action,另一种是直接通过父应用传递的setGlobalState方法修改,该方法需要在Vue的入口文件中设置Vue实例的全局方法。

vue-app/src/main.js

  const { container, getGlobalState } = props;
  Vue.prototype.getGlobalState = getGlobalState

点击父应用添加任务,在Vue应用中进行双向绑定。 此处输入图片的描述

在Vue子应用中修改任务 此处输入图片的描述

能够在兄弟应用中展示最新的任务内容 此处输入图片的描述

最后未来让主应用能正确识别微应用暴露出来的一些信息,微应用的打包工具需要增加如下配置。

vue-app/vue.config.js

const { name } = require('./package');
module.exports = {
  devServer: {
    // open: true,
    // hot: true,
    // compress: true,
    // disableHostCheck: true,
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
  },
  configureWebpack: {
    output: {
      library: `${name}-[name]`,
      libraryTarget: 'umd', // 把微应用打包成 umd 库格式
      jsonpFunction: `webpackJsonp_${name}`,
    },
  },
};

🎉彩蛋🎉:在项目根目录中配置了npm-run-all的命令,可以很方便的启动主应用与子应用。

编辑 (opens new window)
#qiankun
上次更新: 2021/08/12, 23:57:28
PicX+Github图床

PicX+Github图床→

最近更新
01
Promise
08-18
02
Vue查漏补缺
08-16
03
筛选组件
08-14
更多文章>
Theme by Vdoing | Copyright © 2021-2021 AlienGao | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×