模块联邦(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'))

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

相关推荐
Rverdoser1 小时前
unocss 一直热更新打印[vite] hot updated: /__uno.css
前端·css
Bang邦1 小时前
使用nvm管理Node.js多版本
前端·node.js·node多版本管理
podoor1 小时前
wordpress不同网站 调用同一数据表
前端·wordpress
LJ小番茄2 小时前
Vue 常见的几种通信方式(总结)
前端·javascript·vue.js·html
黑狼传说2 小时前
前端项目优化:极致最优 vs 相对最优 —— 深入探索与实践
前端·性能优化
장숙혜2 小时前
前端-CDN的理解及CDN一些使用平台
前端
FakeOccupational3 小时前
nodejs 007:错误npm error Error: EPERM: operation not permitted, symlink
前端·npm·node.js
奶糖 肥晨3 小时前
react是什么?
前端·react.js·前端框架
亦舒.3 小时前
JSDelivr & NPM CDN 国内加速节点
前端·npm·node.js
代码搬运媛3 小时前
code eintegrity npm err sha512
前端·npm·node.js