Giter Club home page Giter Club logo

wl-mfe's Introduction

wl-mfe

基于 vue3.0-beta 及 qiankun2.0 极速尝鲜!微前端进阶实战项目。
项目地址: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 找不到start命令,那你可能是mac or linux,那就进入目录一个一个下载运行吧。
另:执行批量服务耗时较久,请耐心等待,init与build成功会在控制台提示,serve稍加等待或刷新浏览器即可。

实战详解todo

  • 主应用基座构建
  • 子应用构建
  • 微应用间通信
  • 跨应用通信与vuex结合
  • 发布上线

主应用基座构建

主应用需要用到elementui,暂时使用vue2.0+qiankun2.0版本。vue3.0beta体验在下面【子应用构建】章节

主应用项目主要在5个文件:utils文件夹,app.vueappRegister.jsmain.jsrender.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 id来注册子应用盒子的,因此只用qiankun2.0的话不需要考虑render注测子应用盒子的情况,下面那两个dom和script里的props都可以不要!只留一个<div id="subapp-viewport"></div>即可!
另外:注册子应用时每个子应用都可以指定一个不同的container,因此如果想做每个子应用的keep-alive,则可能需要每个子应用对应一个<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的render方法。
因为qiankun1.0需要在注册vue实例时显式的将appContent传入app.vue,如果你不用qiankun1.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;
  }
}

此处是给兼容qiankun1.0 registerMicroApps方法render字段一种方案,事实上升级到2.0完全无压力,因此建议不需要留下臃肿的render方法。

将注册子应用的逻辑抽离到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,看过我上个文章的朋友都知道我将props分为那几个模块,实际上,我真正用到的可能就是主应用请求获取下来的routesrouterbase下发给子应用。
  2. 关于appStore方法,我是将官方通信机制提取至utils文件夹下的app-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尝鲜,大部分时间都用在找3.0的api上,还有许多未解决的问题,比如往vue实例上挂载方法,手动注销vue是啥api,怎么注册插件比如elementUI等,后续会慢慢补充。
这里使用vue3.0beta实现demo效果已经没问题!

vuecli初始化项目并升级至vue3.0beta

默认你已经装了vuecli3.0以上版本

vue crate subapp-ui

cd subapp-ui

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

在这里不单独赘述vue3.0beta的特性,对此网上有许多文章。我们在实践我们微前端的需求实际应用中取逐渐解开它的神秘面纱!

改造子应用vue.config.js文件

注意设置publicPath、端口号与注册子应用时一致
注意开发时开启headers跨域头信息
注意output按照规定格式打包

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}`
    }
  }
};

在main.js同级添加 public-path.js

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

在main.js同级添加 life-cycle.js 统一设置子应用生命周期逻辑

我在这里区分微前端环境和单独运行的加载机制,并引入官方通信方法

注意:3.0beta的实例化方法为 createApp,并且注册路由是通过连续use的方法,详见下放代码:
注意:3.0的router实例化方法为 createRouter, 注意history模式通过createWebHistory方法实现,并且此方法接受一个参数表示路由前缀
注意:3.0的vuex倒是变化不大,但暂未弄明白3.0的mapGetters,mapActions的使用方法

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 };

在 utils/app-store.js 编写应用间的通信逻辑处理

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;

这里分别导出了setStateappStore两个方法,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 beta 基本特性体验
  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会执行发布脚本,根据提示选择要发布到的服务器和要发布的应用,按指示选择后回车执行即可。 注意为保持发布脚本的精简,默认你要发布的应用已经打包出了dist目录。

常规多端口nginx配置

根据qiankun的子应用注册规则,给每个子应用分配一个端口,nginx正常配置监听多个端口即可。 详细配置见_nginx目录下general-port.conf

双端口nginx配置(git切换到dual-port分支)

有些项目应用场景及客户要求限制,无法根据子应用的数量无节制的开放端口,因此尝试将主应用独立一个端口,子应用共用一个端口的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', // 子应用的路由前缀
      },
    ],
 )

注意: entry由端口地址变成了端口地址+此子应用的路径(//localhost:2751/ui)。且注意这个/ui路径后面要讲到

修改子应用

  1. 取消public-path.js,不再使用这个打包路径,下面这段代码删除
if (window.__POWERED_BY_QIANKUN__) {
  __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
  1. 修改vue.config.js文件publicPath字段,并和注册时的entry保持一致
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)

此项目部署阿里云练手教程

  1. 将全部微应用和config、_server应用install
  2. 打包所有微应用
  3. 将config/deploy.js的服务器ip账号密码端口路径改为你的
  4. 在根目录执行npm run publish选择服务器和子应用,后点击回车部署到服务器
  5. 登陆服务器,将_nginx/wl-mfe.conf inclouds到你的nginx.conf重启nginx使其生效
  6. 将_server复制到服务器中并使用pm2运行
  7. 检查服务器防火墙和安全组有没有开放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', // 子应用的路由前缀
      },
    ],
 )

注意: entry由端口地址变成了主应用端口地址+此子应用的路径(//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();跳转,在vue子应用中,使用router-link跳转会报错;使用router.push()会造成刷新;使用router.replace无异常

友情链接

微前端 & qiankun

可能是你见过最完善的微前端解决方案
微前端的核心价值
目标是最完善的微前端解决方案 - qiankun 2.0
qiankun

vue3.0beta

Vue 3.0 全家桶抢先体验

打赏咖啡

如果你有心,可以请作者喝杯咖啡,或者推荐一份好工作

wl-mfe's People

Contributors

hql7 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

wl-mfe's Issues

子应用引入地图部分接口跨域

子应用引入百度地图(vue-baidu-map),调用百度地图的部分方法,出现跨域问题,会导致地图无法正常使用,尝试过在父页面的library里面新增共通组件下发给子应用,仍然会出现跨域问题,大牛能不能指导一下

主应用切换至vue3后,两个router冲突BUG

主应用+子应用都使用vue3并使用vue-router-next。点击子应用页面内的,会触发三次子应用的router.beforeEach钩子,且不会触发主应用router的router.beforeEach钩子。

多页签模式

有没有考虑过多页签模式,一个页签对应一个子应用;子应用页签切换时候保留状态。

产线同域名部署

请问产线nginx如何配置,在同域下
刷新会直接跳转到子应用,基座的布局不在了

BUG-无限加载

在这个目录进行刷新 会无限 刷新重加载,可以解决一下吗。其它目录无此问题

关于hash路由

老哥,你有试过主应用是hash吗,我是试了hash不可以呢

ie11兼容不了

qiankun2.0不是说支持ie11了吗?怎么在ie11上还是跑不起来?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.