在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:hql7/wl-micro-frontends开源软件地址:https://github.com/hql7/wl-micro-frontends开源编程语言:Vue 50.1%开源软件介绍:wl-micro-frontends [wl-qiankun]本项目采用 vue + qiankun 实践微前端落地。 另有微前端进阶实战项目:基于 vue3.0-beta 及 qiankun2.0 极速尝鲜!wl-mfe 在线地址项目启动npm run cinit
npm run init
下载依赖,因为是批量下载所有应用下的依赖,推荐cinit节省下载时间
npm run serve
运行项目,同样,批量运行所有应用会耗时较久,浏览器页面自动打开后请稍家等待,然后刷新即可
npm run build
打包项目,打包所有应用 微前端 qiankun微前端是什么、为什么要做微前端、qiankun 是什么这些笔者将不再叙述。(文末有彩蛋~) 实战教程目录详解鉴于 qiankun 文档只有寥寥十几行,这里做一个尽量详细的实战示例描述:
微前端主应用与子应用如何构建构建主应用
// 导入qiankun内置函数
import {
registerMicroApps, // 注册子应用
runAfterFirstMounted, // 第一个子应用装载完毕
setDefaultMountApp, // 设置默认装载子应用
start, // 启动
} from 'qiankun';
let app = null;
/**
* 渲染函数
* appContent 子应用html
* loading 如果主应用设置loading效果,可不要
*/
function render({ appContent, loading } = {}) {
if (!app) {
app = new Vue({
el: '#container',
router,
store,
data() {
return {
content: appContent,
loading,
};
},
render(h) {
return h(App, {
props: {
content: this.content,
loading: this.loading,
},
});
},
});
} else {
app.content = appContent;
app.loading = loading;
}
}
/**
* 路由监听
* @param {*} routerPrefix 前缀
*/
function genActiveRule(routerPrefix) {
return (location) => location.pathname.startsWith(routerPrefix);
}
// 调用渲染主应用
render();
// 注册子应用
registerMicroApps(
[
{
name: 'vue-aaa',
entry: '//localhost:7771',
render,
activeRule: genActiveRule('/aaa'),
},
{
name: 'vue-bbb',
entry: '//localhost:7772',
render,
activeRule: genActiveRule('/bbb'),
},
],
{
beforeLoad: [
(app) => {
console.log('before load', app);
},
], // 挂载前回调
beforeMount: [
(app) => {
console.log('before mount', app);
},
], // 挂载后回调
afterUnmount: [
(app) => {
console.log('after unload', app);
},
], // 卸载后回调
}
);
// 设置默认子应用,参数与注册子应用时genActiveRule("/aaa")函数内的参数一致
setDefaultMountApp('/aaa');
// 第一个子应用加载完毕回调
runAfterFirstMounted(() => {});
// 启动微服务
start(); 注意, 主应用的 el 绑定 dom 为#container,因此你也需要修改一下 index.hrml 模板中的 id
<template>
<div id="root" class="main-container">
<div class="main-container-menu"></div>
<!-- 子应用盒子 -->
<div id="root-view" class="app-view-box" v-html="content"></div>
</div>
</template>
<script>
export default {
name: 'root-view',
props: {
loading: Boolean,
content: String,
},
};
</script>
主应用可以不配置 module.exports = {
devServer: {
port: 3333,
},
}; 更多的设置: const port = 7770; // dev port
module.exports = {
// publicPath: './',
devServer: {
// host: '0.0.0.0',
hot: true,
disableHostCheck: true,
port,
overlay: {
warnings: false,
errors: true,
},
headers: {
'Access-Control-Allow-Origin': '*',
},
},
}; 注意这里,vue 项目中,主应用设置 构建子应用
import Vue from "vue";
import VueRouter from "vue-router";
import App from "./App.vue";
import "./public-path";
import routes from "./router";
Vue.config.productionTip = false;
// 声明变量管理vue及路由实例
let router = null;
let instance = null;
// 导出子应用生命周期 挂载前
export async function bootstrap(props) {
console.log(props)
}
// 导出子应用生命周期 挂载前 挂载后
**注意,实例化路由时,判断当运行在qiankun环境时,路由要添加前缀,前缀与主应用注册子应用函数genActiveRule("/aaa")内的参数一致**
export async function mount(props) {
router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__ ? "/aaa" : "/",
mode: "history",
routes
});
instance = new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
}
// 导出子应用生命周期 挂载前 卸载后
export async function unmount() {
instance.$destroy();
instance = null;
router = null;
}
// 单独开发环境
window.__POWERED_BY_QIANKUN__ || mount(); **注意:**重要的事情说两遍-- 实例化路由时,判断当运行在 qiankun 环境时,路由要设置 base 参数,参数值与主应用注册子应用函数 genActiveRule("/aaa")内的参数一致。 **注意:**上面小小的修改了 router.js 导出的内容 & 引入了一个叫
const routes = [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'a-about',
component: () => import('../views/About.vue'),
},
];
export default routes; 不再导出 rouer 实例而是导出路由数据
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
const path = require('path');
const { name } = require('./package');
function resolve(dir) {
return path.join(__dirname, dir);
}
const port = 7771; // dev port
module.exports = {
outputDir: 'dist',
assetsDir: 'static',
filenameHashing: true,
devServer: {
// host: '0.0.0.0',
hot: true,
disableHostCheck: true,
port,
overlay: {
warnings: false,
errors: true,
},
headers: {
'Access-Control-Allow-Origin': '*',
},
},
// 自定义webpack配置
configureWebpack: {
resolve: {
alias: {
'@': resolve('src'),
},
},
output: {
// 把子应用打包成 umd 库格式
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
},
},
}; 注意 output 必须按照规定格式要求配置; 经过上述改造,一个简易的微前端环境就草草建成了,是不是很简单,你是不是已经跃跃欲试了? 直接上手跑代码 --> wl-micro-frontends(wl-qiankun) 当然,一个基础的微前端架子建成后,我们还有一些无法绕过的问题要处理,下面将其中部分逐一讲解。 主应用与子应用通信(静态,无法监测到值变化)父子应用通信在上述所建微前端应用中,父子间的通信是极其普遍且无法绕过的需求,而 qiankun 在这方面当然有所考虑。
registerMicroApps([
{
name: 'app1',
entry: '//localhost:7771',
render,
activeRule: genActiveRule('/app1'),
props: 'mg', // 传递给子应用
},
]);
export async function bootstrap(props) {
console.log(props);
}
qiankun 对于 props 的应用类似于 react 框架的父子组件通信,传入 data 数据供自组件使用,传入 fn 函数给子组件触发向上回调。 按照这个思路我们将主应用的 main.js 和子应用的 main.js 都改造一番(此改造会在后续主应用下发子应用资源章节重构):
// ...
// 定义传入子应用的数据
let msg = {
data: {
auth: false,
},
fns: [
function LOGOUT_(data) {
alert('父应用返回信息:' + data);
},
],
};
// 注册子应用
registerMicroApps([
{
name: 'app1',
entry: '//localhost:7771',
render,
activeRule: genActiveRule('/app1'),
props: msg, // 将定义好的数据传递给子应用
},
]);
//... 在注册应用时将定义好的
// ...
export async function bootstrap(props = {}) {
Array.isArray(props.fns) &&
props.fns.map((i) => {
Vue.prototype[i.name] = i[i.name];
});
}
// ... 我们这里在 bootstrap 函数里将接收到的 props 参数内的函数挂在 vue 原型上方便使用,你也可以在其他导出的生命周期函数内得到 props 并按照你的设想去处理。 主、子应用间动态通信(动态,各应用间实时监听,同步数据)经过上述处理后我们仍会遇到一个问题:即需要改变状态的数据如果通信,上面只是在注册子应用时传递了一次数据,而数据改变后又不能再注册一遍子应用。这个时候就需要应用间动态通信了,比如处理主应用登陆后的用户身份改变,或者 token 之类。 这个问题是考虑微前端架构的人都会遇到,当然 qiankun 官方也在日程上规划的有官方通信机制,但是鉴于一次又一次的时间推迟及维护人手短缺,这里本文作者使用rxjs来作为应用间通信的方案。
import { Subject } from 'rxjs'; // 按需引入减少依赖包大小
const pager = new Subject();
export default pager;
import pager from './util/pager'; // 导入应用间通信介质:呼机
pager.subscribe((v) => {
// 在主应用注册呼机监听器,这里可以监听到其他应用的广播
console.log(`监听到子应用${v.from}发来消息:`, v);
store.dispatch('app/setToken', v.token); // 这里处理主应用监听到改变后的逻辑
});
let msg = {
// 结合下章主应用下发资源给子应用,将pager作为一个模块传入子应用
data: store.getters, // 从主应用仓库读出的数据
components: LibraryUi, // 从主应用读出的组件库
utils: LibraryJs, // 从主应用读出的工具类库
emitFnc: childEmit, // 从主应用下发emit函数来收集子应用反馈
pager, // 从主应用下发应用间通信呼机
};
registerMicroApps(
// 注册子应用
[
{
name: 'subapp-ui',
entry: '//localhost:6651',
render,
activeRule: genActiveRule('/ui'),
props: msg, // 将上面数据传递给子应用
},
]
);
export async function bootstrap({ components, utils, emitFnc, pager }) {
Vue.use(components); // 注册主应用下发的组件
Vue.prototype.$mainUtils = utils; // 把工具函数挂载在vue $mainUtils对象
Object.keys(emitFnc).forEach((i) => {
// 把mainEmit函数一一挂载
Vue.prototype[i] = emitFnc[i];
});
pager.subscribe((v) => {
// 在子应用注册呼机监听器,这里可以监听到其他应用的广播
console.log(`监听到子应用${v.from}发来消息:`, v);
// store.dispatch('app/setToken', v.token) // 在子应用中监听到其他应用广播的消息后处理逻辑
});
Vue.prototype.$pager = pager; // 将呼机挂载在vue实例
}
methods: { // 在某个应用里调用.next方法更新数据,并传播给其他应用
callParentChange() {
this.myMsg = "但若不见你,阳光也无趣"
全部评论
专题导读
上一篇:wl-ui/wl-mfe: 基于vue3+koa2+qiankun2的微前端后台管理系统项目实战发布时间:2022-06-07下一篇:Emurgo/yoroi-frontend: Yoroi Wallet - Cardano ADA Wallet - Your gateway to the f ...发布时间:2022-06-07热门推荐
热门话题
阅读排行榜
|
请发表评论