ModuleFederation是在Vue3中使用Vue2构建的微服务
前言:备注:本文基于对webpackModuleFederation的一定理解。一般在使用模块联合的时候都会使用同一个版本,比如Vue2components是当时在Vue2中使用的,但是我为什么在Vue3项目中使用Vue2组件其实是历史原因。几个旧的核心项目是使用Vue2编写的。团队有机会在中期和空闲的时候使用Vue3进行重构,但是他们没有这样做。这个阶段已经晚了,项目变得庞大,人员也减少了。最近在维护一个项目,很受折磨。例如,一个.vue文件有3,000行代码。框架设计不合理,不易维护,更不用说多人维护了。所以,我决定花时间重构。Vue3相比Vue2的好处是不言而喻的,所以我们干脆用Vue3+ts+pinia+element-plus+webpack。既然要用到模块联合,我们就用webpack,因为Vite对模块联合的支持还不是很好。1、使用方法:虽然webpack官网没有介绍,但是我在GitHub上找到了ModuleFederationdemo,传送门。首先,我们运行演示。1.拉下代码:https://github.com/module-fed...2.在最外层目录安装并依赖yarn3。进入vue2-in-vue3的目录,执行启动yarnstart的命令,此时会启动两个服务,其中vue3使用了vue2的Button组件:UserHOST(vue3):localhost:3002ProviderREMOTE(vue2):localhost:3001webpack相关配置:vue2webpack配置插件:[...newModuleFederationPlugin({name:'vue2App',filename:'remoteEntry.js',library:{type:'var',name:'vue2App'},exposes:{'./vue2':'./node_modules/vue/dist/vue',//注意点:这里需要暴露Vue,原因后面会解释'./Button':'./src/components/Button',},}),...],vue3webpack配置插件:[...newModuleFederationPlugin({name:'vue3',filename:'remoteEntry.js',remotes:{vue2App:'vue2App@http://localhost:3001/remoteEntry.js',},}),...],vue3项目App.vue文件
utils.js,核心是使用vue2ToVue3方法,使用vue2渲染函数,将vue2组件挂载到指定元素上importVue2from'vue2App/vue2';functionbindSlotContext(target={},context){returnObject.keys(target).map(key=>{constvnode=target[key];vnode.context=context;returnvnode;});}/*核心*将vue2组件转换为DOM。(官方说明)*理解:这里通过使用vue2的渲染功能,将导入的组件挂载到指定的节点上,巧妙的在vue3中使用vue2的组件。**确实解决了问题,但是在使用的时候会有一些缺点:**1.缺点是:每次使用一个组件,都需要写一个元素来承载。在经常使用的组件中,比如:button,input等常用组件,*一个页面可能会用到很多,所以写的时候有时候需要创建很多元素来承载,所以不适合用在这个scenario**2.使用场景:使用频率低,交互性稍复杂的组件*/exportfunctionvue2ToVue3(WrapperComponent,wrapperId){letvm;return{mounted(){constslots=bindSlotContext(this.$slots,this.__self);vm=newVue2({render:createElement=>{returncreateElement(WrapperComponent,{on:this.$attrs,attrs:this.$attrs,props:this.$props,scopedSlots:this.$scopedSlots,},插槽,);},});vm.$mount(`#${wrapperId}`);},props:WrapperComponent.props,render(){vm&&vm.$forceUpdate();},};}vue2项目,Button.vue,注意:
2.常见问题:1.UncaughtError:Sharedmoduleisnotavailableforeagerconsumption其实webpack官方已经给出了解决方案。具体可以看官方这里解决方法:新建bootstrap.js,将main.js的内容放到bootstrap.js中,在主入口异步加载bootstrap.js//bootstrap.jsimport{createApp}from'vue'importAppfrom'./App.vue'importrouterfrom'./router'createApp(App).use(router).mount('#app')//程序入口:main.js有的叫index.jsimport('./bootstrap.js')按照剧情的发展,这个问题解决了,但是这个时候还是报这个错在之前的其他项目中,我也是使用这种方法来解决这个问题的。为什么这次不行呢?查了一下,发现原因:1、由于我是用vue-cli创建的,vue-cli中有两个默认的环境变量NODE_ENV='development'和NODE_ENV='production',但对我的使用并不满意,所以我加了一个NODE_ENV='test',自定义--mode,如下:package.jsonserve:test":"vue-cli-serviceserve--modetest#根目录下使用三个环境变量文件|--.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',会报错“Sharedmoduleisnotavailableforeagerconsumption”我改了.env.test这样就好了:NODE_ENV='development'#这里改成developmentVUE_APP_API_ENV='test'。这个修正其实对我的项目没有影响,因为原来的项目只有正式和开发两个环境,我多定义了一个环境变量用来表示界面的环境。有时它们可??以用于后端的本地调试。当然你也可以使用cross-env的方式。2、vue2微服务中,使用了vue3不支持的第三方库。vue3不能用element-ui,需要用element-plus。在我的vue2组件库中,部分组件对element-ui进行了二次封装,vue3使用modulefederation使用时,报错,如:找不到el-table等。测试项目中可以正常使用element-plus,然后我导入element-ui看看能不能用,当然不能用,如果支持vue3就不用弄个element-plus了。当然,除了这种情况,可以使用微服务暴露的实用函数,或者自己写的组件。总结:1.首先,我不推荐使用这种方法。不要将其用作最后的手段。2.ModelFederation在vue3中使用vue2组件,会有很多坑和不方便的地方。有精力的话建议换个组件库Forvue3,orvue2,vue3common