模块联邦(Module Federation)的使用

模块联邦(Module Federation)

由Zack Jackson这位工程师提出的一种JavaScript架构,后来被他和其他几个工程师共同编写集成进了webpack5。 所以要使用 模块联邦 webpack 的版本至少5.

vue3 加载vue3的模块

这里我是在同一个项目内使用模块联邦 vue3设置模块

javascript 复制代码
    const { ModuleFederationPlugin } = require('webpack').container
    new ModuleFederationPlugin({
      name: 'workbench',
      filename: 'remoteEntry.js',
      exposes: {
        './button-group': './src/federations/button-group/index.tsx',
        './collapse': './src/federations/collapse/index.tsx',
        './collapse-plan': './src/federations/collapse-plan/index.tsx',
        './jz-tag': './src/federations/jz-tag/index.tsx',
        './plan': './src/federations/plan/index.tsx',
        './tab-bar': './src/federations/tab-bar/index.tsx'
      }
    })

vue3使用模块-webpack-plugin

javascript 复制代码
    new ModuleFederationPlugin({
      name: 'workbench',
      filename: 'remoteEntry.js',
      remotes: {
        workbench: ` workbench@${publicPath}/remoteEntry.js`
      }
    })

使用自己的模块时,不需要指定模块名称

javascript 复制代码
 const JzCollapse = defineAsyncComponent(
  () => import('@/federations/collapse/index')
)
 const JzCollapsePanel = defineAsyncComponent(
  () => import('@/federations/collapse-plan/index')
 )

vue3 加载vue2的模块

设置模块

css 复制代码
  new ModuleFederationPlugin({
    name: 'recruit',
    filename: 'remoteEntry.js', 
    exposes: {
      './vue2': './src/share/index.js',
      "./purposeAdd": "./src/views/purpose/components/insert.vue",
      "./clueAdd": "./src/components/addClub.vue",
    },
    shared: require('./package.json').dependencies,
  })

注意 './vue2': './src/share/index.js',这个人模块是我用来共享项目环境的,共享的代码如下

scala 复制代码
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import store from '@/store/index'
Vue.use(ElementUI)

class ShareVue extends Vue {
  constructor(props) {
    super({ ...props, store })
  }
}
export default ShareVue

引入recruit模块

arduino 复制代码
   new ModuleFederationPlugin({
      name: 'workbench',
      filename: 'remoteEntry.js',
      remotes: {
          // 静态资源地址
          "recruit": "recruit@https://jxxx.xxx.com/remoteEntry.js"}
    })

vue2的模块并不能在vue3中直接使用(其实不同的项目,可能也会有问题),我们先写一个vue2的兼容代码。

php 复制代码
// recruit是联邦模块的名称 这里
import Vue2 from 'recruit/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.
 */
export function vue2ToVue3 (WrapperComponent, wrapperId) {
  if (WrapperComponent.options) {
    WrapperComponent = WrapperComponent.options
  }
  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,
              emit: this.$emit,
              scopedSlots: this.$scopedSlots
            },
            slots
          )
        }
      })
      vm.$mount(`#${wrapperId}`)
    },
    props: WrapperComponent.props,
    render () {
      vm && vm.$forceUpdate()
    },
    destroyed () {
      vm && vm.$destroy()
    }
  }
}

写了一个defineAsyncVue2Component的函数为了在使用的时候方便。

javascript 复制代码
import {
  defineAsyncComponent,
  defineComponent,
  onDeactivated,
  onUnmounted
} from 'vue'
// 生成随机字母 其实没啥用 
import { randomCoding } from '@/tools/tools'
import { vue2ToVue3 } from '@/tools/vue2ToVue3'

// 动态加载模块
export const defineAsyncVue2Component = (componentElm) => {
  async function render () {
    const component = await componentElm()
    const wrapperId = `define_vue_${component.default.name}_${randomCoding(5)}`
    const vue2Component = vue2ToVue3(component.default, wrapperId)
    return defineComponent({
      props: {
        ...vue2Component.props,
        on: {
          type: Object,
          default: () => ({})
        }
      },
      setup (props) {
        onDeactivated(() => {
          vue2Component.destroyed()
        })
        onUnmounted(() => {
          vue2Component.destroyed()
        })
        return () => (
          <>
            <div id={wrapperId}></div>
            <vue2Component {...props} {...props.on}></vue2Component>
          </>
        )
      }
    })
  }
  return defineAsyncComponent(render)
}

在主要代码中使用模块 recruit 是vue2的模块名称

javascript 复制代码
// @ts-ignore
const clueAdd = defineAsyncVue2Component(() => import('recruit/clueAdd'))
// @ts-ignore
const purposeAdd = defineAsyncVue2Component(() => import('recruit/purposeAdd'))

加载组件时网络 的执行情况,其中前三位就是被共享的模块

相关推荐
恋猫de小郭4 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅12 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端