Vue3.0 и qiankun2.0 — это быстрое внедрение, а микроинтерфейс — продвинутый и практичный!

внешний интерфейс

wl-mfe

Основанный на vue3.0-beta и qiankun2.0, он очень быстр для первых пользователей! Передовой продвинутый практический проект Micro.
адрес проекта:wl-mfe

Для получения подробного вводного руководства и плана освобождения микро-фронтенд боя, пожалуйста, перейдите к другой моей статье:Для прочтения этой статьи достаточно фронтенд-микро боя - проект Vue.
адрес проекта:wl-micro-frontends [wl-qiankun] && онлайн-доступ

окончательный эффект

wl-mfe

Начало проекта

npm run yinit    // 使用yarn下载依赖,推荐
npm run cinit    // 使用cnpm下载依赖
npm run init     // 或 使用npm下载依赖

npm run serve    // 运行全部项目
yarn serve y     // yarn运行全部项目

npm run build     // 打包全部项目
yarn build y      // 打包全部项目

npm run publish   // 执行发布脚本

Примечание. Если при загрузке сообщается об ошибке, а команда запуска не может быть найдена в bin/sh, возможно, у вас Mac или Linux, затем войдите в каталог для загрузки и запустите их одну за другой.
Кроме того: для выполнения пакетных служб требуется много времени, пожалуйста, подождите терпеливо, в консоли появится сообщение об успешной инициализации и сборке, подождите некоторое время или обновите браузер.

Подробное объяснение todo в реальном бою

  • Основная сборка приложения
  • Сборка вспомогательного приложения
  • Связь между микроприложениями
  • Связь между приложениями в сочетании с vuex
  • выпуск онлайн

Основная сборка приложения

Основное приложение должно использовать elementui и временно использовать версию vue2.0+qiankun2.0. Опыт vue3.0beta описан в главе [Создание подприложения] ниже.

Основной проект приложения в основном состоит из 5 файлов:utilsпапка,app.vue,appRegister.js,main.js,render.js

Предварительные условия

cnpm i qiankun -S

В главном приложении скачать qiankun, обратите внимание на использование версии 2.0 или выше

Преобразование основного приложения app.vue

<template>
  <div class="main-container-view">
    <el-scrollbar class="wl-scroll">
      <!-- qiankun2.0  container 模式-->
      <div id="subapp-viewport" class="app-view-box"></div>
      <!-- qiankun1.0  render 模式-->
      <div v-html="appContent" class="app-view-box"></div>
      <div v-if="loading" class="subapp-loading"></div>
    </el-scrollbar>
  </div>
</template>

<script>
  export default {
    name: "rootView",
    props: {
      loading: Boolean,
      appContent: String
    }
  };
</script>

Обратите внимание, что qiankun2.0 основан наcontainerИдентификатор dom, соответствующий полю, используется для регистрации блока подприложения, поэтому, если вы используете только qiankun2.0, вам не нужно учитывать ситуацию с блоком подприложения рендеринга.Следующие два dom и scriptpropsВы не можете! единственный<div id="subapp-viewport"></div>Вот и все!
Кроме того: при регистрации подприложений каждое подприложение может указывать разныеcontainer, поэтому, если вы хотите поддерживать активность для каждого подприложения, вам может понадобиться по одному для каждого подприложения.<div id="subapp-viewport-ui"></div>,<div id="subapp-viewport-blog"></div>Коробка

Извлеките созданный метод vue в render.js

import Vue from "vue"
import router from './router'
import store from './store'
import App from './App.vue'

/**
 * @name 提取vue示例化方法
 */
export function vueRender() {
  Vue.config.productionTip = false
  new Vue({
    router,
    store,
    render: h => h(App)
  }).$mount("#main-container");  
}

Почему вы просто изменили этот код сmain.jsВынуть? С одной стороны, это максимально очистить main.js, с другой стороны, чтобы он был совместим с методом рендеринга qiankun1.0.
Поскольку qiankun1.0 должен явно зарегистрировать экземпляр vueappContentПередайте app.vue, если вы не используете версию qiankun 1.0, вам вообще не нужен следующий код:

/**
 * @description 实例化vue,并提供子应用 render函数模式的装载能力
 * @description 如果使用qiankun2.0 版本,只需正常实例化vue即可 不需要存在此render函数
 * @param {Object} param0 
 * @description {String} appContent 子应用内容
 * @description {Boolean} loading 是否显示加载动画(需手动实现loading效果)
 * @param {Boolean} notCompatible true则不兼容qiankun1.0 【此参数为示例添加,实际应用自酌】
 */
export function vueRender({ appContent, loading }, notCompatible) {
  Vue.config.productionTip = false

  // 实际上本实例只用到此if内的代码
  // 本文件其他代码只为做兼容qiankun1.0 render挂载子应用的参考
  if (notCompatible) {
    new Vue({
      router,
      store,
      render: h => h(App)
    }).$mount("#main-container");
    return;
  }

  return new Vue({
    router,
    store,
    data() {
      return {
        appContent,
        loading,
      };
    },
    render(h) {
      return h(App, {
        props: {
          appContent: this.content,
          loading: this.loading
        }
      });
    }
  }).$mount('#main-container');
}

let app = null;

/**
 * @name 提供render装载子应用方法
 * @param {Object} param0 
 * @description {String} appContent 子应用内容
 * @description {Boolean} loading 是否显示加载动画(需手动实现loading效果)
 */
export default function render({ appContent, loading }) {
  if (!app) {
    app = vueRender({ appContent, loading });
  } else {
    app.appContent = appContent;
    app.loading = loading;
  }
}

Вот решение для поля рендеринга, совместимое с методом registerMicroApps qiankun1.0.На самом деле обновление до 2.0 совершенно без стресса, поэтому рекомендуется не оставлять раздутый метод рендеринга.

Извлечь логику регистрации подприложений в appRegister.js

Ниже используется метод, чтобы обернуть все методы, которые необходимо использовать qiankun, чтобы зарегистрированное подприложение могло быть выполнено после получения внутренних данных реестра.

/**
 * @name 启用qiankun微前端应用
 * @param {*} list 
 * @param {*} defaultApp 
 */
const useQianKun = (list, defaultApp) => {
  /**
  * @name 注册子应用
  * @param {Array} list subApps
  */
  registerMicroApps(
    [
       {
        name: 'subapp-ui', // 子应用app name 推荐与子应用的package的name一致
        entry: '//localhost:6751', // 子应用的入口地址,就是你子应用运行起来的地址
        container: '#yourContainer', // 挂载子应用内容的dom节点 `# + dom id`【见上面app.vue】
        activeRule: '/ui', // 子应用的路由前缀
      },
    ],
    {
      beforeLoad: [
        app => {
          console.log('[LifeCycle] before load %c%s', 'color: green;', app.name);
        },
      ],
      beforeMount: [
        app => {
          console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
        },
      ],
      afterUnmount: [
        app => {
          console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
        },
      ],
    },
  )

  /**
   * @name 设置默认进入的子应用
   * @param {String} 需要进入的子应用路由前缀
   */
  setDefaultMountApp('ui');
  /**
   * @name 启动微前端
   */
  start();
  /**
   * @name 微前端启动进入第一个子应用后回调函数
   */
  runAfterFirstMounted(() => {
    console.log('[MainApp] first app mounted');
  });
}

В сочетании с внутренним реестром запросов и распределением полного кода маршрутизации и преобразования данных в подприложения:

import { registerMicroApps, runAfterFirstMounted, setDefaultMountApp, start, initGlobalState } from "qiankun";
import store from "./store";
/**
 * @name 导入render函数兼容qiakun1.0装载子应用方法,如果使用2.0container装载则不需要此方法,此处留着注释代码提供兼容qiankun1.0的示例
 * @description 此处留下注释代码仅为提供兼容qiankun1.0示例
 */
// import render from './render';
/**
 * @name 导入接口获取子应用注册表
 */
import { getAppConfigsApi } from "./api/app-configs"
/**
 * @name 导入消息组件
 */
import { wlMessage } from './plugins/element';
/**
 * @name 导入想传递给子应用的方法,其他类型的数据皆可按此方式传递
 * @description emit建议主要为提供子应用调用主应用方法的途径
 */
import emits from "./utils/emit"
/**
 * @name 导入qiankun应用间通信机制appStore
 */
import appStore from './utils/app-store'
/**
 * @name 声明子应用挂载dom,如果不需要做keep-alive,则只需要一个dom即可;
 */
const appContainer = "#subapp-viewport";
/**
 * @name 声明要传递给子应用的信息
 * @param data 主应要传递给子应用的数据类信息
 * @param emits 主应要传递给子应用的方法类信息
 * @param utils 主应要传递给子应用的工具类信息(只是一种方案)
 * @param components 主应要传递给子应用的组件类信息(只是一种方案)
 */
let props = {
  data: store.getters,
  emits
}

/**
 * @name 请求获取子应用注册表并注册启动微前端
 */
getAppConfigsApi().then(({ data }) => {
  // 验证请求错误
  if (data.code !== 200) {
    wlMessage({
      type: 'error',
      message: "请求错误"
    })
    return;
  }
  // 验证数据有效性
  let _res = data.data || [];
  if (_res.length === 0) {
    wlMessage({
      type: 'error',
      message: "没有可以注册的子应用数据"
    })
    return;
  }
  // 处理菜单并存入主应用Store
  store.dispatch('menu/setMenu', _res);
  // 处理子应用注册表数据。详细数据见 master mock
  let apps = []; // 子应用数组盒子
  let defaultApp = null; // 默认注册应用
  let isDev = process.env.NODE_ENV === 'development'; // 根据开发环境|线上环境加载不同entry
  _res.forEach(i => {
    apps.push({
      name: i.module, // 子应用名
      entry: isDev ? i.devEntry : i.depEntry, // 根据环境注册生产环境or开发环境地址
      container: appContainer,  // 绑定dom
      activeRule: i.routerBase, // 绑定子应用路由前缀
      props: { ...props, routes: i.children, routerBase: i.routerBase } // 将props及子应用路由,路由前缀由主应用下发
    })
    if (i.defaultRegister) defaultApp = i.routerBase; // 记录默认启动子应用
  });
  // 启用qiankun微前端应用
  useQianKun(apps, defaultApp);
})

/**
 * @name 启用qiankun微前端应用
 * @param {*} list 
 * @param {*} defaultApp 
 */
const useQianKun = (list, defaultApp) => {
  /**
  * @name 注册子应用
  * @param {Array} list subApps
  */
  registerMicroApps(
    list,
    {
      beforeLoad: [
        app => {
          console.log('[LifeCycle] before load %c%s', 'color: green;', app.name);
        },
      ],
      beforeMount: [
        app => {
          console.log('[LifeCycle] before mount %c%s', 'color: green;', app.name);
        },
      ],
      afterUnmount: [
        app => {
          console.log('[LifeCycle] after unmount %c%s', 'color: green;', app.name);
        },
      ],
    },
  )

  /**
   * @name 设置默认进入的子应用
   * @param {String} 需要进入的子应用路由前缀
   */
  setDefaultMountApp(defaultApp);

  /**
   * @name 启动微前端
   */
  start();

  /**
   * @name 微前端启动进入第一个子应用后回调函数
   */
  runAfterFirstMounted(() => {
    console.log('[MainApp] first app mounted');
  });
}

/**
 * @name 启动qiankun应用间通信机制
 */
appStore(initGlobalState);

Зарегистрируйте папку utils механизма связи между приложениями

При регистрации суб-приложения выше мы видим, что код передается в суб-приложениеpropsс однимappStoreКоммуникационная функция.

  1. оprops, друзья, прочитавшие мою прошлую статью, знают, что я разделяю пропсы на несколько модулей, на самом деле то, что я действительно использую, может быть тем, что запрашивает основное приложение.routesа такжеrouterbaseДоставлено в субприложения.
  2. оappStoreМетод, я извлек официальный механизм связи к папке Utilsapp-store.js文件,并和vuex相结合。 код показывает, как показано ниже:
import store from "@/store";

/**
 * @name 启动qiankun应用间通信机制
 * @param {Function} initGlobalState 官方通信函数
 * @description 注意:主应用是从qiankun中导出的initGlobalState方法,
 * @description 注意:子应用是附加在props上的onGlobalStateChange, setGlobalState方法(只用主应用注册了通信才会有)
 */
const appStore = (initGlobalState) => {
  /**
   * @name 初始化数据内容
   */
  const { onGlobalStateChange, setGlobalState } = initGlobalState({
    msg: '来自master初始化的消息',
  });

  /**
   * @name 监听数据变动
   * @param {Function} 监听到数据发生改变后的回调函数
   * @des 将监听到的数据存入vuex
   */
  onGlobalStateChange((value, prev) => { 
    console.log('[onGlobalStateChange - master]:', value, prev);
    store.dispatch('appstore/setMsg', value.msg)
  });

  /**
   * @name 改变数据并向所有应用广播
   */
  setGlobalState({
    ignore: 'master',
    msg: '来自master动态设定的消息',
  });
}

export default appStore;

[Примечание: если связь не зарегистрирована в основном приложении, метод связи не может быть получен в дополнительном приложении]

Модернизация main.js

Наконец мы подошли к последнему шагу.После того, как все преобразования основного приложения завершены, вносим его в main.js и выполняем:

/**
 * @name 统一注册外部插件、样式、服务等
 */
import './install'

/**
 * @name 微前端基座主应用vue实例化
 * @description 为了兼容 qiankun1.0 的render函数装载子应用能力
 * @description 2.0版本正常实例化vue即可,不需要此render函数
 * @description qiankun registerMicroApps方法 render用到,如果使用container装载子应用,无需此render函数
 * @deprecated 本示例只针对 qiankun2.0 因此只留下注释后的代码在此提醒各位读者如何兼容qiankun1.0
 */
/* import render from './render';
render({ loading: true }) */
import { vueRender } from './render'
vueRender({}, true)

/**
 * @name 注册微应用并启动微前端
 */
import './appRegister'

Сборка вспомогательного приложения

Подприложение использует vue3.0beta для ранних пользователей, и большая часть времени уходит на поиск API 3.0.Есть еще много нерешенных проблем, таких как метод монтирования на экземпляре vue, ручное отключение того, что API является vue , как зарегистрировать такие плагины, как elementUI и т. д., которые будут добавляться постепенно в будущем.
Здесь нет проблем с использованием vue3.0beta для достижения демо-эффекта!

Vuecli инициализирует проект и обновления в Vue3.0beta

По умолчанию вы установили vuecli 3.0 или более позднюю версию.

vue crate subapp-ui

cd subapp-ui

// 在此之前都是正常创建项目,到这里执行下面命令会以插件的形式将项目升级至3.0
vue add vue-next 

Я не буду здесь подробно останавливаться на особенностях vue3.0beta, статей в интернете много. Мы постепенно разгадываем тайну этого в практическом применении наших требований к микро-фронтенду!

Модифицируйте файл подприложения vue.config.js

Обратите внимание, что установка publicPath и номера порта такая же, как и при регистрации подприложения.
Обратите внимание, что междоменная информация заголовка заголовков включена во время разработки.
Обратите внимание, что выходные данные упакованы в указанном формате.

const { name } = require("./package");
const port = 6751; // dev port
const dev = process.env.NODE_ENV === "development";

module.exports = {
  publicPath: dev ? `//localhost:${port}` : "/",
  filenameHashing: true,
  devServer: {
    hot: true,
    disableHostCheck: true,
    port,
    overlay: {
      warnings: false,
      errors: true
    },
    headers: {
      "Access-Control-Allow-Origin": "*"
    }
  },
  // 自定义webpack配置
  configureWebpack: {
    output: {
      // 把子应用打包成 umd 库格式
      library: `${name}-[name]`,
      libraryTarget: "umd",
      jsonpFunction: `webpackJsonp_${name}`
    }
  }
};

if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}



import { createApp } from "vue";
import { createRouter, createWebHistory } from "vue-router";
import App from "./App.vue";
import store from "./store";
import selfRoutes from "./router/routes";

/**
 * @name 导入自定义路由匹配方法
 */
import routeMatch from "./router/routes-match";
/**
 * @name 导入官方通信方法
 */
import appStore from "./utils/app-store";

const __qiankun__ = window.__POWERED_BY_QIANKUN__;
let router = null;
let instance = null;

/**
 * @name 导出生命周期函数
 */
const lifeCycle = () => {
  return {
    /**
     * @name 微应用初始化
     * @param {Object} props 主应用下发的props
     * @description  bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发
     * @description 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等
     */
    async bootstrap(props) {
      console.log('props:', props)
      /* props.emits.forEach(i => {
        Vue.prototype[`$${i.name}`] = i;
      }); */
    },
    /**
     * @name 实例化微应用
     * @param {Object} props 主应用下发的props
     * @description 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
     */
    async mount(props) {
      // 注册应用间通信
      appStore(props);
      // 注册微应用实例化函数
      render(props);
    },
    /**
     * @name 微应用卸载/切出
     */
    async unmount() {
      instance.$destroy?.();
      instance = null;
      router = null;
    },
    /**
     * @name 手动加载微应用触发的生命周期
     * @param {Object} props 主应用下发的props
     * @description 可选生命周期钩子,仅使用 loadMicroApp 方式手动加载微应用时生效
     */
    async update(props) {
      console.log("update props", props);
    }
  };
};

/**
 * @name 子应用实例化函数
 * @param {Object} props param0 qiankun将用户添加信息和自带信息整合,通过props传给子应用
 * @description {Array} routes 主应用请求获取注册表后,从服务端拿到路由数据
 * @description {String} 子应用路由前缀 主应用请求获取注册表后,从服务端拿到路由数据
 */
const render = ({ routes, routerBase, container } = {}) => {
  router = createRouter({
    history: createWebHistory(__qiankun__ ? routerBase : "/"),
    routes: __qiankun__ ? routeMatch(routes, routerBase) : selfRoutes
  });
  instance = createApp(App).use(router).use(store).mount(container ? container.querySelector("#app") : "#app");
};

export { lifeCycle, render };

import store from "@/store";
import { DataType } from "wl-core"

/**
 * @name 声明一个常量准备将props内的部分内容储存起来
 */
const STORE = {};

/**
 * @name 启动qiankun应用间通信机制
 * @param {Object} props 官方通信函数
 * @description 注意:主应用是从qiankun中导出的initGlobalState方法,
 * @description 注意:子应用是附加在props上的onGlobalStateChange, setGlobalState方法(只用主应用注册了通信才会有)
 */
const appStore = props => {
  /**
   * @name 监听应用间通信,并存入store
   */
  props?.onGlobalStateChange?.(
    (value, prev) => {
      console.log(`[onGlobalStateChange - ${props.name}]:`, value, prev)
      store.dispatch('appstore/setMsg', value.msg)
    },
    true
  );
  /**
   * @name 改变并全局广播新消息
   */
  props?.setGlobalState?.({
    ignore: props.name,
    msg: `来自${props.name}动态设定的消息`,
  });

  /**
   * @name 将你需要的数据存起来,供下面setState方法使用
   */
  STORE.setGlobalState = props?.setGlobalState;
  STORE.name = props.name;
};

/**
 * @name 全局setState方法,修改的内容将通知所有微应用
 * @param {Object} data 按照你设定的内容格式数据 
 */
const setState = (data) => {
  if (!DataType.isObject(data)) {
    throw Error('data必须是对象格式');
  }
  STORE.setGlobalState?.({
    ignore: STORE.name,
    ...data
  })
}

export {
  setState
}
export default appStore;

Экспортировано сюдаsetState,appStoreдва метода,appStoreнаверхуlife-cycle.jsЗарегистрируйте использование глобальной связи в файле жизненного цикла, затемsetStateГде мы собираемся использовать это? Давайте продолжим чтение

Измените main.js вспомогательного приложения.

Экспорт функций жизненного цикла и предоставление отдельной логики операций

import "./public-path";
import { lifeCycle, render } from "./life-cycle";

/**
 * @name 导出微应用生命周期
 */
const { bootstrap, mount, unmount } = lifeCycle();
export { bootstrap, mount, unmount };

/**
 * @name 单独环境直接实例化vue
 */
const __qiankun__ = window.__POWERED_BY_QIANKUN__;
__qiankun__ || render();

Попрактикуйтесь в файле .vue подприложения.

здесь, вviews/index.vueДелайте практические упражнения Требовать:

  1. Опыт работы с основными функциями бета-версии Vue 3.0.
  2. Получайте глобальные сообщения и публикуйте сообщения в других микро-приложениях.

Перейдите непосредственно к коду:

<template>
  <div class="home">
    <div class="msg-box">
      <div class="msg-title">这里是子应用:</div>
      <div class="msg-context">{{selfMsg}}</div>
    </div>
    <div class="msg-box">
      <div class="msg-title">来自其他微应用的消息:</div>
      <div class="msg-context">{{vuexMsg}}</div>
    </div>
    <div class="msg-box">
      <div class="msg-ipt-box">
        <input class="msg-ipt" type="text" v-model="formMsg" placeholder="请输入你想广播的话" />
      </div>
      <div class="msg-btn-box">
        <button class="msg-btn" @click="handleVuexMsgChange">发送广播</button>
      </div>
    </div>
  </div>
</template>

<script>
import { ref, computed, getCurrentInstance } from "vue";
import { setState } from "@/utils/app-store";

export default {
  name: "Home",
  setup() {
    /**
     * @name 通过getCurrentInstance方法得到当前上下文
     */
    const { ctx } = getCurrentInstance();
    /**
     * @name 定义一个初始数据
     */
    const selfMsg = ref("subapp-ui");
    /**
     * @name 定义一个计算属性,返回vuex中的数据
     */
    const vuexMsg = computed(() => ctx.$store.getters.msg);
    /**
     * @name 定义一个表单元素v-model绑定的变量
     */
    const formMsg = ref("");

    /**
     * @name 定义一个广播事件
     */
    const handleVuexMsgChange = () => {
      /**
       * @name 注意:在setup内部使用定义的变量,需要用**.value取值!
       */
      setState({
        msg: formMsg.value
      });
    };

    // 注意变量和事件都要return出来
    return {
      selfMsg,
      vuexMsg,
      formMsg,
      handleVuexMsgChange
    };
  }
};
</script>

выпуск онлайн

Используйте файлы сценариев для повышения эффективности публикации

Выполнить в корневом каталогеnpm run publishСценарий публикации будет выполнен, выберите сервер для публикации и приложение для публикации в соответствии с подсказками, а затем нажмите Enter для выполнения. Обратите внимание, что для простоты сценария выпуска приложение, которое вы хотите выпустить, по умолчанию упаковано в каталог dist.

Общая многопортовая конфигурация nginx

согласно сqiankunПравила регистрации подприложений назначают порт каждому подприложению, а nginx можно настроить для обычного прослушивания нескольких портов. Подробную настройку см._nginxПод содержаниемgeneral-port.conf

Двухпортовая конфигурация nginx (переключение git на двухпортовую ветку)

Некоторые сценарии приложений проекта и требования заказчика ограничены, а открывать порты без проверки по количеству подприложений невозможно, поэтому попробуйте настроить nginx с отдельным портом для основного приложения и общим портом для подприложений . Подробную настройку см._nginxПод содержаниемdual-port.conf

Использование двухпортовой конфигурации nginx требует внесения некоторых изменений в конфигурацию из предыдущего руководства.

Измените данные о регистрации зарегистрированного приложения RegistermicroApps в основном приложении

 registerMicroApps(
    [
       {
        name: 'subapp-ui', // 子应用app name 推荐与子应用的package的name一致
        entry: 'http://192.168.1.100:2751/ui', // 子应用的入口地址
        container: '#yourContainer', // 挂载子应用内容的dom节点 `# + dom id`【见上面app.vue】
        activeRule: '/ui', // 子应用的路由前缀
      },
    ],
 )

Примечание. Запись меняется с адреса порта на адрес порта + путь к этому подприложению (//localhost:2751/ui). И обратите внимание, что этот путь /ui будет упомянут позже.

Изменить вложенные приложения

  1. Отменить public-path.js, больше не использовать этот путь упаковки, удалить следующий код
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
  1. Измените поле publicPath файла vue.config.js и сохраните его в соответствии с записью во время регистрации.
module.exports = {
  publicPath: 'http://192.168.1.100:2751/ui'
  ...
}
  1. Префикс маршрутизации подприложения должен быть «/ui», что совпадает с предыдущими двумя.
const render = ({ routerBase } = {}) => {
  router = new VueRouter({
    base: __qiankun__ ? routerBase : "/",
    mode: "history",
    routes: []
  });
  1. Настройте nginx (см.: _nginx/dual-port.conf)

Этот проект развертывает руководство Alibaba Cloud - на руководянии

  1. Все применяются и конфигурация, _server aphall
  2. Пакет всех микроприложений
  3. Измените путь к порту пароля учетной записи IP-адреса сервера config/deploy.js на свой
  4. Выполните npm run publish в корневом каталоге, чтобы выбрать сервер и суб-приложение, а затем нажмите Enter, чтобы выполнить развертывание на сервере.
  5. Войдите на сервер, добавьте _nginx/wl-mfe.conf в свой nginx.conf и перезапустите nginx, чтобы он вступил в силу.
  6. Скопируйте _server на сервер и запустите с pm2
  7. Проверьте брандмауэр сервера и Group Group не имеет открытых портов 2750 2751,3000

На этом этапе все ресурсы подприложения могут быть сопоставлены и перенаправлены на один порт через конфигурацию nginx.

Онлайн-предварительная версия микроинтерфейса развертывания с двумя портами

На данный момент, простая в использовании практика применения микро-фронтенда vue3.0 + qiankun2.0 завершена, приходите и попробуйте! адрес проекта:Github;

Конфигурация nginx с одним портом

В сценарии спроса используется конфигурация с двумя портами. Идя дальше, некоторые экстремальные среды публикации открывают только один порт или запрещают открытые междоменные требования, требуя, чтобы основное приложение и все подприложения находились в одном домене! Подробную настройку см._nginxПод содержаниемsingle-port.conf(Идея с одним портом примерно такая же, еще не проверял)

Изменить данные зарегистрированного подприложения registerMicroApps в основном приложении.

 registerMicroApps(
    [
       {
        name: 'subapp-ui', // 子应用app name 推荐与子应用的package的name一致
        entry: 'http://192.168.1.100:2750/ui', // 子应用的入口地
        container: '#yourContainer', // 挂载子应用内容的dom节点 `# + dom id`【见上面app.vue】
        activeRule: '/ui', // 子应用的路由前缀
      },
    ],
 )

Примечание. Запись меняется с адреса порта на адрес порта основного приложения + путь к этому подприложению (//localhost:2750/ui). Обратите внимание на разницу с двойным портом

Изменить вложенные приложения

Базовые и двойные порты были всегда, разница только в том, что publicPath стал основным портом приложения + путь подприложения

module.exports = {
  publicPath: 'http://192.168.1.100:2750/ui'
  ...
}

На данный момент один и тот же порт основного и вспомогательного приложений может быть достигнут с помощью конфигурации nginx. (См.: _nginx/single-port.conf)

Непроверенные, но предсказуемые проблемы с конфигурацией с одним портом

  1. Это может привести к тому, что qiankun не сможет проверить жизненный цикл, экспортируемый субприложением.
  2. Может привести к тому, что обновление в маршрутизации подприложения станет независимым запущенным подприложением.
  3. Может войти в подприложение, но не в основное приложение, что приводит к белому экрану

Если возникают эти проблемы, вы можете запустить основное + вспомогательное приложение на одном порту, настроив конфигурацию nginx и т. д. Потому что это доказано как теоретически, так и практически.

Меры предосторожности

  1. Используйте window.history.pushState() в основном приложении; jump, в подприложении vue, используйте router-link для перехода, чтобы сообщить об ошибке; используйте router.push(), чтобы вызвать обновление; используйте router.replace без исключения

Ссылки

Микро интерфейс и qiankun

Вероятно, самое полное решение для микроинтерфейса, которое вы когда-либо видели.
Основная ценность микрофронтенда
Стремится стать наиболее полным решением для микроинтерфейса — qiankun 2.0
qiankun

vue3.0beta

Ранний доступ к Vue 3.0 Family Bucket

чаевые кофе

Если у вас есть сердце, вы можете попросить автора чашечку кофе или порекомендовать хорошую работу.