前言:

备注:本文基于对webpack Module Federation有一定了解的情况下

  1. 一般情况下使用模块联邦都是会使用相同的版本,如Vue2的组件时在Vue2中使用,但我为什么会在Vue3项目中去使用Vue2的组件呢,其实是因为历史原因。好几个老的核心的项目都是使用Vue2来写的,在中期以及空闲的时候团队是有机会使用Vue3去重构,但是并没有这样做,到了现在这个阶段已经太晚了,项目变得庞大,人员也减少了。

  2. 最近在维护一个项目,被折磨得不行,比如一个.vue文件有3千行代码,框架设计不合理,不易于维护,更不易于多人维护。所以,我决定抽空去重构。

  3. Vue3相对于Vue2的好处不言而喻,故索性使用Vue3 + ts + pinia + element-plus + webpack,由于要使用module federation,所以使用webpack,因为Vite 对module federation支持还是不太好


一、如何使用:

虽然webpack官网并没有介绍,但是在GitHub中找到了Module Federation demo,传送门

首先,我们把demo跑起来。

1.将代码拉下来:https://github.com/module-federation/module-federation-examples.git

2.在最外层目录安装依赖

yarn

3.进入vue2-in-vue3这个目录,执行命令启动

```shell
yarn start
```
这时候会启动两个服务,其中vue3使用了vue2的Button组件:
- 使用方HOST (vue3): [localhost:3002](http://localhost:3002/)
- 提供方REMOTE (vue2): [localhost:3001](http://localhost:3001/)

webpack相关配置:

vue2 webpack配置

plugins: [
...
new ModuleFederationPlugin({
name: 'vue2App',
filename: 'remoteEntry.js',
library: { type: 'var', name: 'vue2App' },
exposes: {
'./vue2': './node_modules/vue/dist/vue', // 注意点:这里需要把vue暴露出去,原因后面讲
'./Button': './src/components/Button',
},
}),
...
],

vue3 webpack配置

plugins: [
...
new ModuleFederationPlugin({
name: 'vue3',
filename: 'remoteEntry.js',
remotes: {
vue2App: 'vue2App@http://localhost:3001/remoteEntry.js',
},
}),
...
],

vue3项目 App.vue文件

<template>
<div>
<h3>Vue3 App</h3>
<Content :count="count"/> <!--这里跟我们使用普通组件有一点区别
1、正常情况我们只需要这样写: <Button @btnClick="inc"/>
2、而这里则需要使用一个元素来将挂载组件
-->
<div id="vue2Button"></div>
<vue2-button @btnClick="inc"/> </div>
</template> <script>
import { ref } from "vue";
import Content from "./components/Content";
import { vue2ToVue3 } from './utils';
import Button from 'vue2App/Button'; export default {
components: {
Content,
// 通过一个方法,将vue2的组件转为vue3的组件,并挂载在id为'vue2Button'的元素上
vue2Button: vue2ToVue3(Button, 'vue2Button'),
},
setup() {
const count = ref(0);
const inc = () => {
count.value++;
}; return {
count,
inc,
};
}
};
</script>

utils.js,核心在于使用vue2ToVue3方法,使用vue2渲染函数将vue2组件挂载到指定元素

import Vue2 from 'vue2App/vue2';

function bindSlotContext(target = {}, context) {
return Object.keys(target).map(key => {
const vnode = target[key];
vnode.context = context;
return vnode;
});
} /* 核心
* Transform vue2 components to DOM.(官方注释)
* 理解:这里通过使用vue2的渲染函数,将导入的组件挂载在指定的节点上,巧妙地让vue2的组件能在vue3中使用
* * 确实解决了问题,但是在使用的时候会有一些弊端:
* * 1、存在弊端:每使用一个组件就需要写一个元素去承载,在使用比较频繁的组件中,如:button、input 等常用组件,
* 一个页面可能使用非常多个,那么在书写的时候就需要创建很多元素去承载,所以在这种场景下不太适合使用
* * 2、使用场景:使用频率低,有交互的、稍微复杂一点的组件
*/
export function vue2ToVue3(WrapperComponent, wrapperId) {
let vm;
return {
mounted() {
const slots = bindSlotContext(this.$slots, this.__self);
vm = new Vue2({
render: createElement => {
return createElement(
WrapperComponent,
{
on: this.$attrs,
attrs: this.$attrs,
props: this.$props,
scopedSlots: this.$scopedSlots,
},
slots,
);
},
});
vm.$mount(`#${wrapperId}`);
},
props: WrapperComponent.props,
render() {
vm && vm.$forceUpdate();
},
};
}

vue2项目,Button.vue, 注意点:

<template>
<button @click="click">vue2 button click</button>
</template> <script> export default {
methods: {
click() {
this.$emit("btnClick");
// vue3 事件命名需要使用大驼峰,并且前以on开头
this.$emit("onBtnClick");
}
}
}
</script>

二、常见问题:

一、Uncaught Error: Shared module is not available for eager consumption

其实webpack官方是有给出解决方法的,详情见这里

官方给出的解决方法:新建一个bootstrap.js ,将main.js的内容放到bootstrap.js中,在主入口异步去加载bootstrap.js

// bootstrap.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app') // 程序入口:main.js 有的是叫做 index.js
import ('./bootstrap.js')

按照故事情节发展,这个问题就解决了,但是这时候我依旧还是报这个错。在以前的其它项目中,我也是使用这种方式解决这个问题,为什么这一次不行了?

经过排查,我定位到了原因:

1、由于我是使用vue-cli创建的,vue-cli中默认2个环境变量 NODE_ENV='development'和 NODE_ENV='production',但是不满足我使用,我增加了一个 NODE_ENV='test',自定义了 --mode,如下:

package.json

"serve:test": "vue-cli-service serve --mode test",
// 在根目录中使用了三个环境变量文件
|_.env.development
|_.env.production
|_.env.test

.env.development代码:

NODE_ENV = 'development'
VUE_APP_API_ENV = 'dev'

.env.production:

NODE_ENV = 'production'
VUE_APP_API_ENV = 'prod'

.env.test:

NODE_ENV = 'test'
VUE_APP_API_ENV = 'test'

问题就出现在.env.test中,如果NODE_ENV = 'test'的话则会报错“Shared module is not available for eager consumption

我把.env.test改为如下就好了:

NODE_ENV = 'development'  # 这里要改为development
VUE_APP_API_ENV = 'test'

改正这样其实对于我的项目也没有影响,因为本来项目就只有正式和开发两个环境,我定义多了一个环境变量用来表示接口的环境,有时候可以给后端在本地调试使用,当然大家也可以使用cross-env的方式。

二、在vue2微服务中,使用了vue3不支持的第三方库,element-ui 在vue3中不能使用,需要使用element-plus

在我的vue2组件库中,有一部分组件对element-ui进行了二次封装,在vue3使用模块联邦去使用的时候就报错了,如:无法找到el-table 等。

测试element-plus在项目中是正常使用的,那我就导入element-ui看看能不能用,当然是不可用的,如果支持vue3的话,就不用弄一个element-plus了。

当然,除了这种情况以外,微服务暴露的工具函数,或者自己手写的组件,都是可以使用的。

总结:

1、首先,本人不太推荐使用这种方式,万不得已不要用

2、Model Federation 在vue3中去使用vue2的组件,会有很多坑,以及不方便的地方,建议有精力的话把组件库改为vue3的,或者vue2 、 vue3通用的

Module Federation 模块联邦 在Vue3中使用Vue2搭建的微服务的更多相关文章

  1. 032 搭建搜索微服务01----向ElasticSearch中导入数据--通过Feign实现微服务之间的相互调用

    1.创建搜索服务 创建module: Pom文件: <?xml version="1.0" encoding="UTF-8"?> <proje ...

  2. .NET Core 中的 Swagger 应用与微服务场景下的Swagger Api 集成显示

    Swagger 与 OpenAPI 的历史来源: Swagger 项目于 2015 年捐赠给 OpenAPI Initiative,此后被称为 OpenAPI.这两个名称可以互换使用.但是," ...

  3. 在django中如何从零开始搭建一个mock服务

    mock概念 mock 就是模拟接口返回的一系列数据,用自定义的数据替换接口实际需要返回的数据,通过自定义的数据来实现对下级接口模块的测试.这里分为两类测试:一类是前端对接口的mock,一类是后端单元 ...

  4. docker微服务部署之:四、安装docker、docker中安装mysql和jdk1.8、手动构建镜像、部署项目

    docker微服务部署之:三,搭建Zuul微服务项目 1.Centos7安装Docker 详见:Centos7安装Docker 2.Docker中安装jdk1.8 详见:使用Docker构建jdk1. ...

  5. 在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务

    在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务 https://procodeguide.com/programming/polly-in-aspnet-core ...

  6. PACT 在微服务架构中的用途是什么?

    PACT 是一个开源工具,允许测试服务提供者和消费者之间的交互,与合同隔离, 从而提高微服务集成的可靠性. 微服务中的用法 用于在微服务中实现消费者驱动的合同. 测试微服务的消费者和提供者之间的消费者 ...

  7. Module Federation原理剖析

    [转自团队掘金原文: https://juejin.im/post/6895324456668495880] 为什么需要学习webpack5 module Federation原理呢?因为EMP微前端 ...

  8. 【设计模式】module(模块)模式

    写在前面 最近刚接触到设计模式, <head first设计模式>里有一篇文章,是说使用模式的心智, 1.初学者"心智" :"我要为HELLO WORLD找个 ...

  9. 安卓与Unity交互之-Android Studio创建Module库模块教程

    安卓开发工具创建Module库 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分 ...

随机推荐

  1. 【我的面试-01】Web前端开发实习岗-面试题总结

    简单开头 首先技术面试官会根据简历里所写的项目和个人掌握技术栈提问(我不知道已经改过多少次简历了,因为前期投简历是真的是沉在茫茫大海,捞漂流瓶都捞不到的那种) 我的技术栈:(Vue还在苦苦的自学当中, ...

  2. 使用Pure Pipes来替换HTML里面的纯函数

    <ul *ngFor="let item of list"> <li>{{show(item.label)}}</li> </ul> ...

  3. Redis缓存雪崩、缓存穿透、缓存击穿

    缓存雪崩 Redis中的缓存数据是有过期时间的,当在同一时间大量的缓存同时失效时就会造成缓存雪崩. 解决方案 1.设置Redis中的key永不过期,缺点是会占用很多内存 2.使用Redis的分布式锁S ...

  4. Blazor和Vue对比学习(进阶2.2.3):状态管理之状态共享,Blazor的依赖注入和第三方库Fluxor

    Blazor没有提供状态共享的方案,虽然依赖注入可以实现一个全局对象,这个对象可以拥有状态.计算属性.方法等特征,但并不具备响应式.比如,组件A和组件B,都注入了这个全局对象,并引用了全局对象上的数据 ...

  5. 9. 利用Docker快速构建MGR | 深入浅出MGR

    目录 1.安装Docker 2.拉取GreatSQL镜像,并创建容器 2.1 拉取镜像 2.2 创建新容器 2.3 容器管理 3.构建MGR集群 3.1 创建专用子网 3.2 创建3个新容器 3.3 ...

  6. 成为 Apache 贡献者,So easy!

    点击上方蓝字关注 Apache DolphinScheduler Apache DolphinScheduler(incubating),简称"DS", 中文名 "海豚调 ...

  7. NC20242 [SCOI2005]最大子矩阵

    题目链接 题目 题目描述 这里有一个n*m的矩阵,请你选出其中k个子矩阵,使得这个k个子矩阵分值之和最大. 注意:选出的k个子矩阵 不能相互重叠. 输入描述 第一行为n,m,k(1 ≤ n ≤ 100 ...

  8. 自定义View3-水波纹扩散(仿支付宝咻一咻)实现代码、思想

    PS:自定义view篇-水波纹实现 效果:水波纹扩散 场景:雷达.按钮点击效果.搜索等 实现:先上效果图,之前记得支付宝有一个咻一咻,当时就是水波纹效果,实现起来一共两步,第一画内圆,第二画多个外圆, ...

  9. BI如何实现用户身份集成自定义安全程序开发

    统一身份认证是整个 IT 架构的最基本的组成部分,而账号则是实现统一身份认证的基础.做好账号的规划和设计直接决定着企业整个信息系统建设的便利与难易程度,决定着系统能否足够敏捷和快速赋能,也决定了在数字 ...

  10. TFT-eSPI入门使用教程

    一.准备资料 开发板:ESP32-S3 屏驱动是:ST7789_DRIVER 开发环境:VS Code + PlatformIO 注意:以上是我使用的环境,不一定需要和是使用的东西一样,这里主要是学习 ...