在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:wl-ui/wl-mfe开源软件地址:https://github.com/wl-ui/wl-mfe开源编程语言:JavaScript 97.8%开源软件介绍:wl-mfe基于 vue3.0-beta 及 qiankun2.0 极速尝鲜!微前端进阶实战项目。 微前端实战详细入门教程及解放方案请转至我另一篇文章:微前端实战看这篇就够了 - Vue项目篇。 最终效果项目启动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,那就进入目录一个一个下载运行吧。 实战详解todo
主应用基座构建主应用需要用到elementui,暂时使用vue2.0+qiankun2.0版本。vue3.0beta体验在下面【子应用构建】章节 主应用项目主要在5个文件: 前提条件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是根据 将实例化vue方法提取至render.jsimport 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");
} 为什么要仅仅将这段代码从 /**
* @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文件夹
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' |