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

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

相关推荐
加班是不可能的,除非双倍日工资3 小时前
css预编译器实现星空背景图
前端·css·vue3
wyiyiyi4 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip4 小时前
vite和webpack打包结构控制
前端·javascript
excel4 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国5 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼5 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy5 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
ZXT5 小时前
promise & async await总结
前端
Jerry说前后端5 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化
画个太阳作晴天5 小时前
A12预装app
linux·服务器·前端