> For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt.

# 开始使用

在 Modern.js 中使用 Module Federation 我们推荐使用官方插件 `@module-federation/modern-js-v3`。

本章节将会介绍如何通过官方插件搭含生产者应用与消费者应用，我们首先根据 [Modern.js 快速上手](/zh/guides/get-started/quick-start.md) 创建两个应用。

## 安装插件

完成应用创建后，我们分别为两个项目安装插件：


```sh [npm]
npm add @module-federation/modern-js-v3
```

```sh [yarn]
yarn add @module-federation/modern-js-v3
```

```sh [pnpm]
pnpm add @module-federation/modern-js-v3
```

```sh [bun]
bun add @module-federation/modern-js-v3
```

```sh [deno]
deno add npm:@module-federation/modern-js-v3
```

## 注册插件

安装插件后，你需要在 `modern.config.js` 中注册插件：

```ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js-v3';

export default defineConfig({
  plugins: [appTools(), moduleFederationPlugin()],
});
```

## 生产者导出模块

接下来，我们先修改生产者的代码，导出 Module Federation 模块。

我们创建 `src/components/Button.tsx` 文件，导出一个 Button 组件：

```tsx title="src/components/Button.tsx"
import React from 'react';

export const Button = () => {
  return <button type="button">Remote Button</button>;
};
```

随后，在项目根目录添加 `module-federation.config.ts`，配置 Module Federation 模块的名称、共享依赖和导出内容：

```ts title="module-federation.config.ts"
import { createModuleFederationConfig } from '@module-federation/modern-js-v3';

export default createModuleFederationConfig({
  name: 'remote',
  manifest: {
    filePath: 'static',
  },
  filename: 'static/remoteEntry.js',
  exposes: {
    './Button': './src/components/Button.tsx',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
});
```

:::tip
在上述代码块中，我们为 Module Federation 导出的 `manifest` 和 `remoteEntry.js` 都设置了 `static` 前缀，这是因为 Modern.js 要求将所有需要暴露的资源都放在 `static/` 目录下，Modern.js 的服务器在生产环境时也只会托管 `static/` 目录。
:::

另外，我们还需要修改 `modern.config.ts`，为生产者提供一个开发环境的端口，让消费者可以通过此端口访问生产者的资源：

```ts title="modern.config.ts"
import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js-v3';

export default defineConfig({
  server: {
    port: 3051,
  },
  plugins: [appTools(), moduleFederationPlugin()],
});
```

## 消费者使用模块

现在，我们修改消费者的代码，使用生产者导出的模块。

在项目根目录添加 `module-federation.config.ts`，配置 Module Federation 模块的名称、共享依赖和使用的远程模块：

```ts title="module-federation.config.ts"
import { createModuleFederationConfig } from '@module-federation/modern-js-v3';

export default createModuleFederationConfig({
  name: 'host',
  remotes: {
    remote: 'remote@http://localhost:3051/static/mf-manifest.json',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
});
```

`mf-manifest.json` 是生产者在打包后产出的文件，包含了生产者导出的所有模块信息。

我们创建新的路由文件 `src/routes/remote/page.tsx`，引入生产者模块：

```tsx title="src/routes/remote/page.tsx"
import React, { Suspense, type JSX } from 'react';
import { Button } from 'remote/Button';

const Index = (): JSX.Element => {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <Button />
      </Suspense>
    </div>
  );
};

export default Index;
```

此时 `remote/Button` 引入会出现类型错误，这是因为本地没有远程模块的类型。Module Federation 2.0 提供了 [类型提示](https://module-federation.io/zh/guide/basic/type-prompt.html) 的能力，会在生产者构建时自动生成远程模块的类型定义，在消费者构建时自动下载。

为此我们需要在 `tsconfig.json` 中添加新的 `path`，保证类型生效：

```json title="tsconfig.json"
{
  "compilerOptions": {
    "paths": {
      "*": ["./@mf-types/*"]
    }
  }
}
```

:::tip
在消费者中，我们通过 `remote/Button` 来引用远程模块。这里简单介绍下这个路径具体代表了什么，可以先将它抽象成 `[remoteAlias]/[remoteExpose]`。

第一段 `remoteAlias` 是生产者在消费者中的别名，它是消费者 `module-federation.config.ts` 中配置的 `remotes` 字段的 `key`：

```ts
{
  remotes: {
    [remoteAlias]: '[remoteModuleName]@[URL_ADDRESS]',
  }
}
```

这里我们也将远程地址抽象为 `[remoteModuleName]@[URL_ADDRESS]`，`@` 前的部分必须对应生产者的模块名。

第二段 `remoteExpose` 是生产者在 `module-federation.config.ts` 中配置的 `exposes` 字段的 `key`。
:::

## 启动应用

现在，生产者应用和消费者应用都已经搭建完毕，我们可以在本地运行 `modern dev` 启动两个应用。

启动后，消费者中对生产者模块的引入，不会再出现抛错，类型已经下载到消费者应用中。

:::note
修改生产者代码后，消费者会自动拉取生产者的类型。
:::

访问 `http://localhost:8080/remote`，可以看到页面中已经包含了生产者的远程模块 `Button` 组件。

我们也可以在本地执行 `modern serve` 来模拟生产环境。

因为 Module Federation 插件会自动读取 Modern.js 的 `output.assetPrefix` 配置作为远程模块的访问地址，而该值在生产环境下构建后默认是 `/`。如果不做特殊处理，消费者将从自己的域名下拉取远程模块的入口文件。我们可以添加如下配置：

```ts
import { appTools, defineConfig } from '@modern-js/app-tools';
import { moduleFederationPlugin } from '@module-federation/modern-js-v3';

// https://modernjs.dev/en/configure/app/usage
export default defineConfig({
  server: {
    port: 3051,
  },
  output: {
    // Now this configuration is only used in the local when you run modern serve command.
    // If you want to deploy the application to the platform, use your own domain name.
    // Module federation will automatically write it to mf-manifest.json, which influences consumer to fetch remoteEntry.js.
    assetPrefix: 'http://127.0.0.1:3051',
  },
  plugins: [appTools(), moduleFederationPlugin()],
});
```

现在，在生产者中运行 `modern build && MODERN_MF_AUTO_CORS=true modern serve`，在消费者中运行 `modern build && modern serve`，即可在本地模拟生产环境，访问到远程模块。

:::tip
在使用 `modern serve` 命令时，需要在启动生产者项目时携带 `MODERN_MF_AUTO_CORS=true` 环境变量，以自动处理跨域问题，确保消费者可以正常访问生产者的远程模块资源。
:::

上述用例可以参考：[Modern.js & Module Federation 基础用法示例](https://github.com/web-infra-dev/modern.js/tree/main/examples/module-federation/base)。

## 相关文档

- [Module Federation 官方文档](https://module-federation.io/zh/guide/framework/modernjs.html)
