Module Federation

核心能力: Module Federation 允许在一个应用程序(称为 Host)中动态运行另一个应用程序(称为 Remote)的代码,并共享这些应用程序之间的依赖关系(如 React, Vue, Lodash 等)。

相当于,你的主应用(比如 app.com)在运行时,可以从一个完全独立的、可能部署在不同服务器上的应用(比如 components.com)“借用”一个组件(比如 ProductCard)来渲染,就像这个组件是你自己应用的一部分一样。

Remote工程准备

// vite.config.js
import federation from '@originjs/vite-plugin-federation'

const exposes = {
  './components':  resolve(__dirname, 'src/components/index.js')
} 

...
 plugins: [
      ...,
      federation({
        name: 'remote-xxx-comps', // 子应用名称
        filename: 'remoteEntry.js', // 远程入口文件
        // 暴露给主应用的模块:动态扫描 src/components 下的所有 .vue
        exposes,
        shared: ['vue', 'vue-router'], // 共享依赖,避免重复加载
      })
 ]
...

这里我将此Remote工程中的所有components组件共享分发给其他工程,统一维护管理。

Host工程引入

// vite.config.js
import federation from '@originjs/vite-plugin-federation'

...
 plugins: [
   federation({
        name: 'main-app',
        remotes: {
          'remote-xxx-comps': APP_REMOTE_COMP + '/assets/remoteEntry.js' 
                              // -- APP_REMOTE_COMP 远程地址
        },
        shared: {
          vue: {
            singleton: true,
            requiredVersion: '^3.2.0'
          },
          'vue-router': {
            singleton: true,
            requiredVersion: '^4.0.0'
          }
        }
      })
 ]
...
// main.js
// 动态加载远程组件并全局注册
async function loadRemoteComponents() {
  try {
    // 加载远程组件库
    const remoteComponents = await import('remote-universal-ilComps/components')

    const { install } = remoteComponents.default || remoteComponents

    // 全局注册所有远程组件
    if (install && typeof install === 'function') {
      install(app)
    } else {
      // 手动批量注册
      const components = remoteComponents.default || remoteComponents
      Object.keys(components).forEach(name => {
        app.component(`${name}`, components[name])
      })
    }
  } catch (error) {
    console.error('Failed to load remote components:', error)
  }
}

async function initApp() {
  await loadRemoteComponents()
  app
    .use(i18n)
    .use(router)
    .use(pinia)
    .mount('#app')
}

initApp()

引入后在应用程序,进行全局自动引用, 在页面中直接使用。