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

# 插件 API

Modern.js 的 Runtime 插件允许您在应用程序的 React 代码运行时扩展和修改其行为。通过 Runtime 插件，您可以轻松地执行初始化任务、实现 React 高阶组件（HOC）封装等功能。

:::info

Runtime 插件需要通过 `src/modern.runtime.ts` 中的 [`plugins`](/zh/configure/app/runtime/plugins.md) 字段配置。

:::

## 插件结构

一个典型的 Runtime 插件如下所示：

```ts
import type { RuntimePlugin } from '@modern-js/runtime';

const myRuntimePlugin = (): RuntimePlugin => ({
  name: 'my-runtime-plugin',
  setup: api => {
    // 使用 api 注册钩子
    api.onBeforeRender(context => {
      console.log('Before rendering:', context);
    });

    api.wrapRoot(App => {
      return props => (
        <MyContextProvider>
          <App {...props} />
        </MyContextProvider>
      );
    });
  },
});

export default myRuntimePlugin;
```

- `name`： 插件的唯一标识符。
- `setup`： 函数接收一个 `api` 对象，该对象提供了所有可用的 Runtime 插件 API。

## API 概览

Runtime 插件 API 主要分为以下几类：

- **信息获取**：获取运行时配置和钩子函数。
- **生命周期钩子**：在应用渲染的不同阶段执行自定义逻辑。

### 信息获取

#### `api.getRuntimeConfig`

获取用户在 `modern.runtime.ts` 文件中定义的运行时配置。

- **用法**:

```ts
const config = api.getRuntimeConfig();
console.log(config.myCustomSetting);
```

:::warning

此方法返回的是用户配置的副本，修改返回值不会影响原始配置。
:::

#### `api.getHooks`

获取可用于手动触发的钩子函数。

- **类型：**

```ts
() => {
  onBeforeRender: {
    call: (context: any) => Promise<void>;
  }
  // 其他钩子...
};
```

- **用法：**

```ts
const hooks = api.getHooks();
await hooks.onBeforeRender.call(myContext);
```

### 生命周期钩子

#### `api.onBeforeRender`

在应用渲染（包括服务器端渲染和客户端渲染）之前执行。您可以在此钩子中执行数据预取、修改渲染上下文等操作。

- **类型：**

  ```ts
  type OnBeforeRenderFn<RuntimeContext> = (
    context: RuntimeContext,
  ) => Promise<void> | void;
  ```

  `RuntimeContext` 包含当前请求的上下文信息，如请求对象、响应对象等。

- **用法：**

  ```ts
  api.onBeforeRender(async context => {
    const data = await fetchData(context.req);
    context.data = data; // 将数据添加到上下文中
  });
  ```

:::warning

- 此钩子在每次渲染前都会执行，因此应避免执行耗时过长的操作。
- 您可以在此钩子中修改 `context` 对象，并将修改后的 `context` 传递给后续的渲染过程。

:::

#### `api.wrapRoot`

允许您使用自定义的 React 组件包裹应用的根组件。常用于添加全局的 Provider、布局组件等。

- **类型：**

  ```ts
  type WrapRootFn = (App: React.ComponentType<any>) => React.ComponentType<any>;
  ```

- **用法：**

  ```ts
  api.wrapRoot(App => {
    const AppWrapper = props => {
      return (
        <MyGlobalProvider>
          <Layout>
            <App {...props} /> {/* 确保传递 props */}
          </Layout>
        </MyGlobalProvider>
      );
    };
    return AppWrapper;
  });
  ```

:::warning

- **务必将 `props` 传递给原始的 `App` 组件**，否则可能导致应用无法正常工作。
- `wrapRoot` 返回的组件会在每次渲染时重新创建，因此应避免在其中定义复杂的逻辑或状态。

:::

## 进阶用法

### 组合使用钩子

您可以组合使用多个钩子来实现更复杂的功能。例如，您可以使用 `onBeforeRender` 获取数据，然后使用 `wrapRoot` 将数据通过 Context 传递给整个应用：

```ts
import { RuntimePlugin, RuntimeContext } from '@modern-js/runtime';
import { useContext, createContext } from 'react';

export const ThemeContext = createContext<{ theme: string } | null>(null);

export const themePlugin = (): RuntimePlugin => {
  return {
    name: 'theme-plugin',
    setup: api => {
      api.onBeforeRender(async context => {
        const userPreference = await fetch('/api/user/theme-settings').then(
          res => res.json(),
        );
        context.data = {
          theme: userPreference.theme,
        };
      });

      api.wrapRoot(App => {
        return props => {
          const context = useContext(RuntimeContext);
          return (
            <ThemeContext.Provider value={context.data}>
              <App {...props} />
            </ThemeContext.Provider>
          );
        };
      });
    },
  };
};
```
