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

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

相关推荐
秦jh_1 小时前
【Linux】多线程(概念,控制)
linux·运维·前端
蜗牛快跑2131 小时前
面向对象编程 vs 函数式编程
前端·函数式编程·面向对象编程
Dread_lxy1 小时前
vue 依赖注入(Provide、Inject )和混入(mixins)
前端·javascript·vue.js
涔溪2 小时前
Ecmascript(ES)标准
前端·elasticsearch·ecmascript
榴莲千丞2 小时前
第8章利用CSS制作导航菜单
前端·css
奔跑草-2 小时前
【前端】深入浅出 - TypeScript 的详细讲解
前端·javascript·react.js·typescript
羡与2 小时前
echarts-gl 3D柱状图配置
前端·javascript·echarts
guokanglun2 小时前
CSS样式实现3D效果
前端·css·3d
咔咔库奇2 小时前
ES6进阶知识一
前端·ecmascript·es6
渗透测试老鸟-九青3 小时前
通过投毒Bingbot索引挖掘必应中的存储型XSS
服务器·前端·javascript·安全·web安全·缓存·xss