0


webpack5模块联邦(Module Federation)使用教程,在vue2中使用

博客示例demo已经上传gitee
Module Federation 可以实现微前端的效果,只是它是模块级的。即一个应用可以引入另外一个应用的模块。例如在a域名下启动的应用里,可以直接引用b域名应用里的资源。也就是说a应用可以异步获取b应用的组件进行使用。

这样就可以将应用分为更小的应用块,头部导航栏、侧边栏、业务逻辑组件都可以分到不同的应用块里开发,同时应用块间间可共享,互相依赖,可实时获取其它应用块构建好的bundle资源。

在入门前,先对一些概念达成共识:

在Module Federation中,每个应用都是一个独立的构建(webpack工程),称为容器。引用远程模块的应用叫作host,被引用模块所在的应用称为remote。

用到的插件:html-webpack-plugin、vue-loader、vue-template-compiler(需要和vue版本一致)、vue-style-loader、css-loader、webpack、webpack-cli、webpack-dev-server
启动webpack服务可使用:

npx webpack serve --open

基础使用

一、配置remote应用,模块联邦插件会根据配置打包出对应的资源。

如下配置会构建向外暴露的三个组件对应的bundle,并暴露给其它应用。如果remote应用自身的入口文件里引用了这三个组件,会按本地依赖关系进行打包,不会与模块联邦共享。也就是说这三个组件会被打包两次,一次是根据模块联邦的配置,另一次是根据自身依赖关系。

remote应用只要在注册ModuleFederationPlugin插件时,定义三个参数

  • name(向外暴露容器名称)
  • filename(连接本容器的入口文件,包含暴露出去组件的及它们的依赖信息)
  • exposes:向外暴露的组件
// remote应用的webpack.config.js配置const path =require('path');const HtmlWebpackPlugin =require('html-webpack-plugin');const{ VueLoaderPlugin }=require('vue-loader');// webpack5中配置模块联邦的插件const ModuleFederationPlugin =require('webpack/lib/container/ModuleFederationPlugin');

module.exports ={mode:'development',entry:'./src/index.js',output:{clean:true,// publicPath: 'http://localhost:3001/' },devServer:{port:'3001'},plugins:[newHtmlWebpackPlugin({template:'./index.html'}),newVueLoaderPlugin(),// 处理vue文件newModuleFederationPlugin({name:'remote_app',// 向外暴露容器名称filename:'remote_index.js',// 连接本容器的入口文件,包含暴露出去组件的及它们的依赖信息exposes:{// 这里暴露了三个vue组件// 它们依赖vue、lodash、本地其它组件// 格式:{remote组件访问路径} :{本地文件路径}'./MyTable':'./src/com/myTable.vue','./MyTopBar':'./src/com/top-bar.vue','./MyText':'./src/com/text.vue'},})],experiments:{// 模块中可在一级作用域直接使用await import('xxx')topLevelAwait:true},module:{rules:[{test:/\.vue$/,loader:'vue-loader'},{test:/\.css$/,use:['vue-style-loader','css-loader']}]},}

二、host应用配置远程组件地址

host端同样需要注册ModuleFederationPlugin插件,只要定义remotes参数来接入远程应用即可。
也可像remote应用那样加上name、filename、exposes参数来向外暴露组件

// host应用的webpack.config.jsconst path =require('path');const HtmlWebpackPlugin =require('html-webpack-plugin');const{ VueLoaderPlugin }=require('vue-loader');// webpack5中配置模块联邦的插件,也可以解构导入const{ ModuleFederationPlugin }=require('webpack').container;

module.exports ={mode:'development',entry:'./src/index.js',output:{clean:true},devServer:{port:3000},plugins:[newHtmlWebpackPlugin({template:'./index.html'}),newVueLoaderPlugin(),newModuleFederationPlugin({remotes:{// {远程app本地别名}: {远程app配置的name}@远程app根地址/{远程app配置的filename}}// remote_index.js里包含的组件会用到的所有第三方依赖// ,这些依赖可能拆分多个文件,具体看远程容器的webpack配置'remote-app':'remote_app@http://localhost:3001/remote_index.js',},})],module:{rules:[{test:/\.vue$/,loader:'vue-loader'},{test:/\.css$/,use:['vue-style-loader','css-loader']}]}}

三、在host里使用远程组件,像异步组件一样使用

远程组件的导入路径由两部分组成:

import('remote-app/MyTopBar')
  1. 一是host应用中,new模块联邦插件时,定义的remotes参数里,对应的远程应用地址的key,如remote-app
  2. 二是remote应用中,new模块联邦插件时,定义的exposes参数里,对应的模块key,如./MyTable
<!-- 
// host:app.vue
// 远程组件对应的bundle需要在运行时被加载
// 所以要通过动态引入,地址格式:
// import('{远程应用本地别名}/{远程应用exposes的key}')
--><template><div><reomteTable></reomteTable><hostHellow></hostHellow></div></template><script>// 引入host应用本地的两个组件import hostLoading from'./com/loading.vue';import hostError from'./com/Error.vue';exportdefault{components:{// 引入本地异步组件hostHellow:()=>import('./com/hellow.vue'),// 引入远程组件,和引入异步组件(动态导入的模块)语法类似// 路径里remote-app是host里配置的,MyTopBar是remote里配的remoteTopBar:()=>import('remote-app/MyTopBar'),reomteTable:()=>({// 远程组件和本地异步组件使用上无差别component:import('remote-app/MyTable'),// 异步组件加载时使用的组件loading: hostLoading,// 展示加载时组件的延时时间。默认值是 200 (毫秒)delay:200,// 加载失败时使用的组件error: hostError,// 如果提供了超时时间且组件加载也超时了,// 则使用加载失败时使用的组件。默认值是:`Infinity`timeout:3000}),}}</script>

到这里已经可以使用远程组件了,但还可以继续优化:

抽离公共依赖

远程组件依赖了vue和lodash,这两个依赖会被打包进remote应用的

remote_index.js

里,当host引入远程组件,会加载加载remote应用生成的

remote_index.js

文件,如果host本地也依赖了vue、lodash,它们会被加载两次

remote如果通过缓存组等配置把vue、lodash打包到了独立bundle,

remote_index.js

就不会把它们打包进来,而是

remote_index.js

在host被加载后,向remote请求这些依赖的bundle

在host、remote应用里,同时配置插件的shared字段,可避免依赖重复加载。这里配置singleton为true,把vue等依赖打包到独立的bundle里,被host、remote两个应用加载:

一、remote的模块联邦插件配置

只要在ModuleFederationPlugin插件多定义shared参数即可

// webpack.config.jsnewModuleFederationPlugin({name:'remote_app',filename:'remote_index.js',exposes:{'./MyTable':'./src/com/myTable.vue','./MyTopBar':'./src/com/top-bar.vue','./MyText':'./src/com/text.vue'},// 暴露出去的组件依赖了vue、lodash,将vue、lodash独立打包,// 与引用这些组件的host容器共享这些依赖,避免多次加载shared:{vue:{singleton:true,},lodash:{singleton:true,}}})

二、host的模块联邦插件配置

同样添加shared参数即可

// webpack.config.jsnewModuleFederationPlugin({remotes:{'remote-app':'remote_app@http://localhost:3001/remote_index.js',},shared:{// 远程和本地主机都要设置一样的vue:{singleton:true,},lodash:{singleton:true,}}})

三、vue等依赖改为动态引入

无论host还是remote应用,vue等依赖只要配置了shared的

 singleton: true

后,这些依赖会被独立打包。

它的弊端在于:

会直接影响本地代码对这些依赖的引入方式,本地代码是无法再静态导入这些依赖的,只能动态导入,如下代码会报错:

// 入口文件 index.js// 如果这样引入vue会报错:Shared module is not available for eager consumption// 这里可以是remote的或者host的,因为两个都配置了singleton: trueimport Vue form 'vue'newVue({// ...})

解决:

把应用的入口文件代码抽离到bootstrap.js里,入口文件再动态引入bootstrap.js。

这样,整个应用都变成了异步导入,原本需要动态引入的依赖可以直接静态引入

  1. 新建bootstrap.js(可叫其它名字)// 静态导入vue并初始化等import Vue from'vue';import App from'./app.vue';// Vue.config.productionTip = falsenewVue({render:h=>h(App),}).$mount('#app')
  2. 入口文件动态引入bootstrap.js模块// index.jsimport('./bootstrap.js')console.log('index 入口文件');
  3. 这里vue等依赖可正常加载了,不再报开头的错,同时bootstrap.js及其导入的依赖里,都可以直接使用静态导入的方式,加载异步组件(动态导入)// 以bootstrap.js引入的app.vue为例// 例如:remoteTopBar = ()=>import('remote-app/MyTopBar') // 可改成 import remoteTopBar from 'remote-app/MyTopBar'<template><div><remoteTopBar></remoteTopBar><hostHellow></hostHellow></div></template><script>import remoteTopBar from'remote-app/MyTopBar';// 动态引入依旧可以使用// const remoteTopBar = () => import('remote-app/MyTopBar');exportdefault{components:{ remoteTopBar,// 本地动态组件hostHellow:()=>import('./com/hellow.vue')}}</script>
标签: webpack 前端 vue.js

本文转载自: https://blog.csdn.net/weixin_50794208/article/details/130730839
版权归原作者 小龟壳@greaclar 所有, 如有侵权,请联系我们删除。

“webpack5模块联邦(Module Federation)使用教程,在vue2中使用”的评论:

还没有评论