# Modern.js

该插件为 Modern.js 提供 Module Federation 配套功能

## 支持

- modern.js ^2.56.1
- 包含服务器端渲染（SSR）

强烈推荐参考这个应用，它充分利用了最佳功能：
[module-federation 示例](https://github.com/module-federation/core/tree/main/apps/modernjs-ssr)

## 快速开始

### 安装

你可以通过如下的命令安装插件：

import { PackageManagerTabs } from '@theme';

<PackageManagerTabs
  command={{
    npm: 'npm add @module-federation/modern-js --save',
    yarn: 'yarn add @module-federation/modern-js --save',
    pnpm: 'pnpm add @module-federation/modern-js --save',
    bun: 'bun add @module-federation/modern-js --save',
  }}
/>

### 应用插件

在 `modern.config.ts` 的 `plugins` 中应用此插件：

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

export default defineConfig({
  dev: {
    port: 3005,
  },
  runtime: {
    router: true,
  },
  // moduleFederationPlugin 是 modern.js 的插件，可以对构建/运行时做一定的修改
  plugins: [appTools(), moduleFederationPlugin()],
});
```

随后创建 `module-federation.config.ts` 文件，并写入需要的配置：

```ts title="module-federation.config.ts"
import { createModuleFederationConfig } from '@module-federation/modern-js';
export default createModuleFederationConfig({
  name: 'host',
  remotes: {
    remote: 'remote@http://localhost:3006/mf-manifest.json',
  },
  shared: {
    react: { singleton: true },
    'react-dom': { singleton: true },
  },
});
```

### 引用类型

在 `modern-app-env.d.ts` 文件增加 `/// <reference types='@module-federation/modern-js/types' />` 以获取运行时 `@@modern-js/runtime/mf` 类型支持。

```diff title='modern-app-env.d.ts'
+ /// <reference types='@module-federation/modern-js/types' />
```

在 `tsconfig.json` 添加 `paths` 以获取生产者的类型：

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

## 服务端渲染（SSR）

:::info 注意
为更好的性能体验，Module Federation X Modern.js SSR 仅支持 stream SSR 。
:::

在 SSR 场景与 CSR 场景中使用 Module Federation 没有任何区别，开发者保持按照原有的开发行为即可。

但为了更好地使用体验，我们提供了配套的函数/组件来帮助开发者更好的使用 Module Federation。

### createRemoteSSRComponent

import Collapse from '@components/Collapse'

<Collapse>
```ts
declare function createRemoteSSRComponent(
  props: CreateRemoteSSRComponentOptions
): (props: ComponentType) => React.JSX.Element;

type CreateRemoteSSRComponentOptions = {
  loader: () => Promise<T>;
  loading: React.ReactNode;
  fallback: ErrorBoundaryPropsWithComponent['FallbackComponent'];
  export?: E;
};

type ComponentType = T[E] extends (...args: any) => any
  ? Parameters<T[E]>[0] extends undefined
    ? Record<string, never>
    : Parameters<T[E]>[0]
  : Record<string, never>;
```
</Collapse>

该函数在加载组件同时，还会帮助注入相应的样式标签/脚本 ，此行为可以帮助避免流式渲染带来的 CSS 闪烁问题以及加速 PID （首屏可交互时间）。

#### 示例

```tsx
import React, { FC, memo, useEffect } from 'react';
import { registerRemotes, createRemoteSSRComponent } from '@modern-js/runtime/mf';
// 在构建插件声明过的 remote 可以直接在顶层 import
import RemoteComp from 'remote/Image';

const RemoteSSRComponent = createRemoteSSRComponent({
  // 在构建插件声明过的 remote 也可以使用此函数加载：loader: () => import('remote/Image'),
  loader: () => loadRemote('dynamic_remote/Image'),
  loading: <div>loading...</div>,
  fallback: ({ error }) => {
    if (error instanceof Error && error.message.includes('not exist')) {
      return <div>fallback - not existed id</div>;
    }
    return <div>fallback</div>;
  },
});

const Product: FC = () => {
  registerRemotes([
    {
      name: 'dynamic_remote',
      entry: 'http://localhost:3008/mf-manifest.json',
    },
  ]);

  const fallback = (err: Error) => {
    if (err.message.includes('does not exist in container')) {
      return <div>404</div>;
    }
    throw err;
  };

  return <>
    <RemoteSSRComponent />
    <RemoteComp />
  </>;
};
export default Product;
```

#### loading

- 类型：`React.ReactNode`
- 是否必填：是
- 默认值：`undefined`

设置模块载入状态。

#### fallback

- 类型：`(({ error }: { error: Error}) => React.ReactElement)`
- 是否必填：是
- 默认值：`undefined`

当组件**加载**或**渲染**失败时，所渲染的容错组件。

注意：当**渲染**失败的时候，该组件仅在客户端渲染此 容错组件。
