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

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

相关推荐
yqcoder10 分钟前
NPM 包管理问题汇总
前端·npm·node.js
程序菜鸟营16 分钟前
nvm安装详细教程(安装nvm、node、npm、cnpm、yarn及环境变量配置)
前端·npm·node.js
bsr198327 分钟前
前端路由的hash模式和history模式
前端·history·hash·路由模式
杨过姑父1 小时前
ES6 简单练习笔记--变量申明
前端·笔记·es6
Sunny_lxm1 小时前
<keep-alive> <component ></component> </keep-alive>缓存的组件实现组件,实现组件切换时每次都执行指定方法
前端·缓存·component·active
咔咔库奇2 小时前
【TypeScript】命名空间、模块、声明文件
前端·javascript·typescript
兩尛3 小时前
订单状态定时处理、来单提醒和客户催单(day10)
java·前端·数据库
又迷茫了3 小时前
vue + element-ui 组件样式缺失导致没有效果
前端·javascript·vue.js
哇哦Q3 小时前
原生HTML集合
前端·javascript·html
SoWhat~3 小时前
随遇随记篇
前端·javascript