前言

因为公司的项目都是通过https://xxxx.yyy.cn/[projectName] 等方式部署的,而且不同项目之前有公用模块,需要使用这些公用模块的话,就需要以下几种方式:

  1. 最简单的方式就是复制/粘贴,但其缺点也相当明显,就是模块有修改时需要两边同时修改。
  2. 通过npm 的方式发包。然后在两个项目中同时引用,但这种方式也有可能在修改的时候忘记在别的项目中更新版本
  3. 通过webpack ModuleFederation, 模块联邦的方式使用。

尝试使用 ModuleFederation

  • Monitor 组件是一个比较正常的页面搜索列表页,这里不列出来了
const Log2 = () => {
  return (
    <div>123</div>
  )
}
const Log3 = () => {
  const [count, setCount] = useState(0);
  return (
    <div onClick={() => setCount(count + 1)}>{count}</div>
  )
}
  • 刚开始看的时候,还觉得挺简单,只要在设置对应的设置即可
// A项目, 在webpack plugin 中配置如下项目
new ModuleFederationPlugin({
  name: 'erp',
  filename: 'erp_remoteEntry.js',
  // 导出使用的模块
  exposes: {
    './Monitor': path.resolve(
      __dirname,
      '../src/pages/report/monitor/data_monitor/index.page.tsx',
    ),
    './Log2': path.resolve(
      __dirname,
      '../src/pages/report/monitor/log2/index.page.tsx',
    ),
    './Log3': path.resolve(
      __dirname,
      '../src/pages/report/monitor/log3/index.page.tsx',
    ),
  },
  shared: [
    {
      react: {
        singleton: true,
        // 这里在我的项目中一定要设置字符串,不能从package.json 直接引过来 dep.react
        requiredVersion: '^16.14.0',
      },
      'react-dom': {
        singleton: true,
        requiredVersion: '^16.14.0',
      },
    },
  ],
}),
// B 项目中使用remote 表示导入远程模块, B项目使用的是umi
// [text](https://umijs.org/docs/max/mf)

const shared = {
  react: {
    singleton: true,
    eager: true,
  },
  'react-dom': {
    singleton: true,
    eager: true,
  },
};

mf: {
  remotes: [
    {
      // 可选,未配置则使用当前 remotes[].name 字段
      aliasName: 'erp',
      name: 'erp',
      entry: 'https://to.the.remote.com/erp_remoteEntry.js',
    },
  ],
  // 配置 MF 共享的模块
  shared,
},
  • 一开始在A项目中配置shared 导致项目build 失败了,因为一开始是通过导入const package = require('./package.json') 然后通过 package.dependence['react'] 去导入的,后面改成用字符串就成功了

  • 因为一开始没有处理shared, A项目B项目的react版本问题,导致在B项目 中引用Log3 组件时报错。 处理shared 可以解决

import React from 'react'
const LogPage = React.lazy(() => import('erp/Log3'));

const App = () => {
  return (
    <React.Suspense fallback={'Loading'}>
      <LogPage />
    </React.Suspense>
  )
}
  • 处理了上面的事情后,发现导入Monitor 组件是有问题的,因为 Monitor 页面本身导入了一些翻译内容,比如 xx-i18n包, 并且这个包是通过script 的方式引入的,所以我们需要在B项目中的webpack 中设置externals

externals

umi 添加plugins, 在config/config.ts 配置plugin, plugins: [require('${projectRoot}/xxxplugin')], 或者设置headScripts

// ${projectRoot}/plugin.ts

import { IApi } from 'umi';

export default (api: IApi) => {
  api.modifyHTML(($) => {
    $('head').append([
      `<script src='xxx.js'></script>`,
      `<script src='others...'></script>`
    ])
    return $;
  });
};
  • 最后因为 Monitor 引用了 A项目中的全局Mobx store,导致加载失败,最后去除才成功引入。但是后面发现样式有问题,估计是antd 的问题,A 项目中用的是antd4, B项目用的是antd5。猜测只要将antd 加入到shared 配置即可,Mobx store 同样

参考链接