vue3+quasar+qiankun

image.png

上图描述了qiankun微前端的运行过程,父子应用之间可以独立运行.没有子应用,不影响父应用的运行.没有父应用,子应用也是可以独立运行.

qiankun官网描述步骤非常详细:https://qiankun.umijs.org/zh/guide/getting-started

父应用(使用的是vue2)

  1. 安装qiankun,npm i qiankun -S
  2. 在vue组件注册(不一定是vue组件,main.js中也可以)
    PointsStore.vue:



  1. router.js
{
    path: 'point-store/*', // 重要,注意增加'/*',路由匹配到point-store/*时,都加载PointsStore.vue
    name: 'PointsStore',
    component: () => import('../views/PointsStore.vue'),
},

微应用(使用的是vue3)

qiankun官网说在应用入口导出生命周期钩子,但是quasar的入口不是main.ts,入口文件是quasar自动生成的,quasar自动生成文件夹:.quasar,其中包含一个项目入口文件client-entry.js

  1. 新增入口文件
$ quasar new boot micro-lifeCycle.js 

参考:https://next.quasar.dev/quasar-cli/boot-files#usage-of-boot-files

  1. micro-lifeCycle.js如下:
import { boot } from 'quasar/wrappers';
// 重要:由于需要在mount的时候需要重新实例化app(即 new Vue),但是quasar应用不是通过new Vue()实现的,而是调用.quasar/client-entry.js内的方法,所以根据.quasar/*新建了src/quasar-init/*
// src/quasar-init/*的内容和./quasar/*的内容几乎是一致的,区别:src/quasar-init/client-entry导出了init方法
import { init } from 'src/quasar-init/client-entry'; 
import * as Types from 'src/store/consts';

// 如果该应用是作为子应用运行
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; // 参考:https://qiankun.umijs.org/zh/faq#a-%E4%BD%BF%E7%94%A8-webpack-%E8%BF%90%E8%A1%8C%E6%97%B6-publicpath-%E9%85%8D%E7%BD%AE
  render();
}
class Actions {
  // 默认值为空 Action
  actions = {
    onGlobalStateChange: () => { },
    setGlobalState: () => { }
  };
  /**
   * 设置 actions
   */
  setActions(actions) {
    this.actions = actions;
  }
  /**
   * 映射
   */
  onGlobalStateChange(...args) {
    return this.actions.onGlobalStateChange(...args);
  }
  /**
   * 映射
   */
  setGlobalState(...args) {
    return this.actions.setGlobalState(...args);
  }
}

const actions = new Actions();

// render第一次和第二次被调用是: if (window.__POWERED_BY_QIANKUN__) { render(); }, // props为{}
// 后面被调用是微前端生命周期钩子的mount: async function mount(props) {render(props);} // props不再事空对象,有container属性和值
function render(props = {}) {
  const { container } = props; // props来自父应用,container是注册子前端时,设置的container
  // container有值,表示子应用找到落脚点了,需要重新初始化;
  // init方法相当于执行了一遍./quasar/client-entry.js
  if (container) {
    init();
    // action的作用:和主应用通信
    actions.setActions(props);
    actions.onGlobalStateChange(state => {
      const { xxx } = state;
      // todo something
    }, true);
  }
}


async function bootstrap() {
  console.log('vue app bootstraped');
}

/**
 * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
 */
async function mount(props) {
  console.log('vue app mount');
  render(props); // 相当于new Vue
}

/**
 * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
 */
async function unmount() {
  console.log('vue app unmount');
  if (window._POINT_STORE_APP_INSTANCE) {
    window._POINT_STORE_APP_INSTANCE.unmount(); // 一定要卸载
  }
}

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

export default boot(({ app, store }) => {
  // 如果有实例,unmount
  if (window._POINT_STORE_APP_INSTANCE) {
    window._POINT_STORE_APP_INSTANCE.unmount();
  }
  window._POINT_STORE_APP_INSTANCE = app;
  window._POINT_STORE_STORE = store; // store保存到window下,如果有用到store的话
})

export { bootstrap, mount, unmount, update }

props:


image.png
  1. src/quasar-init/client-entry.js
    和.quasar/client-entry.js差不多,区别:
// src/quasar-init/client-entry.js
// 封装createQuasarApp且导出
export function init() { 
  createQuasarApp(createApp)

    .then(app => {
      return Promise.all([
        
        import(/* webpackMode: "eager" */ 'boot/i18n'),
        
        import(/* webpackMode: "eager" */ 'boot/axios'),
        
        import(/* webpackMode: "eager" */ 'boot/initApp.ts'),
        
        import(/* webpackMode: "eager" */ 'src/boot/micro-lifeCycle.js')
        
      ]).then(bootFiles => {
        const boot = bootFiles
          .map(entry => entry.default)
          .filter(entry => typeof entry === 'function')

        start(app, boot)
      })
    })
}
// .quasar/client-entry.js
createQuasarApp(createApp)

  .then(app => {
    return Promise.all([
      
      import(/* webpackMode: "eager" */ 'boot/i18n'),
      
      import(/* webpackMode: "eager" */ 'boot/axios'),
      
      import(/* webpackMode: "eager" */ 'boot/initApp.ts'),
      
      import(/* webpackMode: "eager" */ 'boot/./micro-lifeCycle.js')
      
    ]).then(bootFiles => {
      const boot = bootFiles
        .map(entry => entry.default)
        .filter(entry => typeof entry === 'function')

      start(app, boot)
    })
  })
  1. 配置微应用的打包工具
    qiankun配置微应用的打包工具
    quasar.conf.js:
const packageName = require('./package.json').name;
module.exports = configure(function (/* ctx */) {
    return {
        boot: [
            'i18n',
            'axios',
            'initApp.ts',
            './micro-lifeCycle.js'
        ],
        build: {
            ...
            chainWebpack(chain) {
                // 设置入口文件,设置微前端的生命周期钩子
                chain
                    .entry('main')
                    .add('src/boot/micro-lifeCycle.js')
                    .end()
                    .output
                    .library('pointStore')
                    .libraryTarget('umd')
                    .jsonpFunction(`webpackJsonp_${packageName}`);
                // 重要:参考https://qiankun.umijs.org/zh/faq; 如果不设置,那么字体库找不到
                chain.module.rule('fonts').use('url-loader').loader('url-loader').options({}).end();
            },

        },
        devServer: {
            headers: {
                'Access-Control-Allow-Origin': '*' // 重要
            },
            port: 8000,
            ...
        }
        ...
    }
});

  1. 目录结构


    image.png

注意事项

  1. 基座应用的全局样式会影响子应用,子应用的不会影响基座应用
  2. 子应用的字体库需要在build的时候使用loader字体配置:
    chain.module.rule('fonts').use('url-loader').loader('url-loader').options({}).end();

抖音上看到一个图:


image.png

你可能感兴趣的:(vue3+quasar+qiankun)