# 服务器生命周期

> Source: https://strapi.nodejs.cn/cms/plugins-development/server-lifecycle

🌐 Server API: Lifecycle

服务器 API 有 3 个生命周期函数。在应用完全初始化之前，使用 `register()` 声明功能；初始化 Strapi 后，使用 `bootstrap()` 运行逻辑；关闭时，使用 `destroy()` 清理资源。每个函数都接收 `{ strapi }` 作为其参数。

生命周期函数控制插件的服务器端逻辑在 Strapi 应用启动和关闭过程中何时运行。它们与路由、控制器、服务和其他服务器模块一起从[服务器入口文件](/cms/plugins-development/server-api#entry-file)导出。

🌐 Lifecycle functions control when your plugin's server-side logic runs during the Strapi application startup and shutdown sequence. They are exported from the [server entry file](/cms/plugins-development/server-api#entry-file) alongside routes, controllers, services, and other server blocks.

:::prerequisites

在深入了解本页的概念之前，请确保你已经：

🌐 Before diving deeper into the concepts on this page, please ensure you have:

- [创建了一个 Strapi 插件](/cms/plugins-development/create-a-plugin)，
- 已阅读并理解了[服务器 API](/cms/plugins-development/server-api)的基础知识

:::

## 启动序列 {#startup-sequence}

🌐 Startup sequence

了解每个生命周期何时运行可以帮助你把正确的代码放在正确的位置：

🌐 Understanding when each lifecycle runs helps you put the right code in the right place:

<br/>

| 阶段 | 插件中可用的内容 |
| --- | --- |
| 2. 注册 | `strapi` 对象可用，但数据库尚未初始化，路由尚未初始化 |
| 4. 引导 | 完整运行时：数据库已初始化，路由已初始化，服务和内容类型已加载，其他插件可用 |
| 5. 关闭 | 正在关闭；在 Strapi 完成停止之前使用此钩子释放资源 |

:::note

每个生命周期函数在每个插件实例上只会被调用一次。如果在同一个插件实例上第二次调用生命周期（例如在自定义测试中），Strapi 会抛出错误。在正常操作下不会发生这种情况。

🌐 Each lifecycle function is called once per plugin instance. If a lifecycle is called a second time on the same plugin instance (for example in custom tests), Strapi throws an error. This does not occur under normal operation.

:::

## register() {#register}

**类型:** `Function`

`register()` 在启动早期运行，位于数据库初始化和路由初始化之前。

使用 `register()` 来：

🌐 Use `register()` to:

- 注册 [自定义字段](/cms/features/custom-fields#registering-a-custom-field-on-the-server) 的服务器端
- 注册数据库迁移
- 在 Strapi HTTP 服务器上注册服务器中间件（例如 `strapi.server.use(...)`）
- 在引导之前扩展另一个插件的内容类型或接口

```js title="/src/plugins/my-plugin/server/src/register.js"
'use strict';

module.exports = ({ strapi }) => {
  // Register a server-level middleware early in startup
  strapi.server.use(async (ctx, next) => {
    ctx.set('X-Plugin-Version', '1.0.0');
    await next();
  });
};
```
```ts title="/src/plugins/my-plugin/server/src/register.ts"

  // Register a server-level middleware early in startup
  strapi.server.use(async (ctx: any, next: () => Promise<void>) => {
    ctx.set('X-Plugin-Version', '1.0.0');
    await next();
  });
};
```

## bootstrap()

**类型:** `Function`

`bootstrap()` 在模块生命周期注册（插件/API）、数据库初始化、路由初始化和内容 API 操作注册之后运行。

使用 `bootstrap()` 来：

🌐 Use `bootstrap()` to:

- 用初始数据填充数据库
- 使用 `strapi.service('admin::permission').actionProvider.registerMany(...)` 注册管理员 RBAC 操作
- 注册 cron 任务
- 订阅数据库生命周期事件
- 从你的插件或其他插件调用服务
- 设置需要先注册其他插件的跨插件集成

```js title="/src/plugins/my-plugin/server/src/bootstrap.js"
'use strict';

module.exports = async ({ strapi }) => {
  // Register admin RBAC actions for this plugin
  await strapi.service('admin::permission').actionProvider.registerMany([
    {
      section: 'plugins',
      displayName: 'Read',
      uid: 'read',
      pluginName: 'my-plugin',
    },
    {
      section: 'plugins',
      displayName: 'Settings',
      uid: 'settings',
      pluginName: 'my-plugin',
    },
  ]);
};
```
```ts title="/src/plugins/my-plugin/server/src/bootstrap.ts"

  // Register admin RBAC actions for this plugin
  await strapi.service('admin::permission').actionProvider.registerMany([
    {
      section: 'plugins',
      displayName: 'Read',
      uid: 'read',
      pluginName: 'my-plugin',
    },
    {
      section: 'plugins',
      displayName: 'Settings',
      uid: 'settings',
      pluginName: 'my-plugin',
    },
  ]);
};
```

## destroy()

**类型:** `Function`

`destroy()` 在 Strapi 实例关闭时调用。这是可选的。只有在你的插件持有需要显式清理的资源时才实现它。

使用 `destroy()` 来：

🌐 Use `destroy()` to:

- 关闭外部连接（数据库、消息队列、WebSocket 服务器）
- 在 `bootstrap()` 中清除间隔或超时
- 移除在插件生命周期内注册的事件监听器

```js title="/src/plugins/my-plugin/server/src/destroy.js"
'use strict';

module.exports = ({ strapi }) => {
  // Close an external connection opened in bootstrap()
  strapi.plugin('my-plugin').service('queue').disconnect();
};
```
```ts title="/src/plugins/my-plugin/server/src/destroy.ts"

  // Close an external connection opened in bootstrap()
  strapi.plugin('my-plugin').service('queue').disconnect();
};
```

## 最佳实践 {#best-practices}

🌐 Best practices

- **保持 `register()` 轻量化。** 它在完全初始化之前运行。
- **使用 `bootstrap()` 进行数据库读写。** 数据库在引导阶段初始化，而不是在注册阶段。任何对 `strapi.documents()` 的调用或查询数据库的服务都应放在 `bootstrap()` 中。
- **在 `bootstrap()` 中注册管理员 RBAC 操作。** 在 `bootstrap()` 中使用 `strapi.service('admin::permission').actionProvider.registerMany(...)`。这是在权限服务可用时。内容 API 操作在同一阶段由 Strapi 自动注册。
- **始终将资源创建与 `destroy()` 配对。** 如果你的插件在 `bootstrap()` 中打开连接、注册全局定时器或附加进程监听器，请实现 `destroy()` 来清理这些资源。这可以防止在测试和优雅重启期间发生资源泄漏。
- **避免 `register()` 中插件之间的强依赖。** 在注册时，其他插件的注册顺序无法保证。依赖于另一个插件已初始化的跨插件调用应放在 `bootstrap()` 中。
- **优先使用服务而非内联逻辑。** 将非平凡的启动逻辑移入专用的服务方法（例如 `strapi.plugin('my-plugin').service('setup').initialize()`）。这样可以保持生命周期文件的可读性，同时使逻辑易于测试。
