# 生命周期函数

> Source: https://strapi.nodejs.cn/cms/configurations/functions

🌐 Functions

`src/index` 托管全局注册、引导和销毁函数，以在应用生命周期中运行逻辑。

<div className="dont_hide_secondary_bar">

`./src/index.js` 文件（或在 [基于 TypeScript](/cms/typescript) 的项目中为 `./src/index.ts` 文件）包含全局的 [register](#register)、[bootstrap](#bootstrap) 和 [destroy](#destroy) 函数，可用于添加动态和基于逻辑的配置。

🌐 The `./src/index.js` file (or `./src/index.ts` file in a [TypeScript-based](/cms/typescript) project) includes global [register](#register), [bootstrap](#bootstrap) and [destroy](#destroy) functions that can be used to add dynamic and logic-based configurations.

这些函数可以是同步的、异步的或返回一个 promise。

🌐 The functions can be synchronous, asynchronous, or return a promise.

</div>

## 可用模式 {#available-modes}

🌐 Available modes

生命周期函数支持三种执行模式，因此你可以将它们与它们管理的依赖对齐。Strapi 会等待每个函数完成，无论它是正常返回、解析一个 `async` 函数，还是解析一个 Promise，然后才继续启动或关闭过程。

🌐 Lifecycle functions support 3 execution patterns/modes so you can align them with the dependencies they manage. Strapi waits for each function to finish, whether it returns normally, resolves an `async` function, or resolves a promise, before moving on with startup or shutdown.

Strapi 不使用返回值，因此函数应仅在设置或清理完成后才解析（或返回），并在失败时抛出异常或拒绝。

🌐 Return values aren't used by Strapi, so the functions should resolve (or return) only when their setup or cleanup is complete and throw or reject to signal a failure.

### 同步 {#synchronous}

🌐 Synchronous

同步函数运行的逻辑会立即完成，无需等待其他异步任务。

🌐 Synchronous functions run logic that completes immediately without awaiting other asynchronous tasks.

```js
module.exports = {
  register({ strapi }) {
    strapi.log.info('Registering static configuration');
  },
  bootstrap({ strapi }) {
    strapi.log.info('Bootstrap finished without awaiting tasks');
  },
  destroy({ strapi }) {
    strapi.log.info('Server shutdown started');
  }
};
```

```ts

  register({ strapi }) {
    strapi.log.info('Registering static configuration');
  },
  bootstrap({ strapi }) {
    strapi.log.info('Bootstrap finished without awaiting tasks');
  },
  destroy({ strapi }) {
    strapi.log.info('Server shutdown started');
  }
};
```

### 异步 {#asynchronous}

🌐 Asynchronous

异步函数使用 `async` 关键字来 `await` 任务，例如 API 调用或数据库查询，然后 Strapi 才继续。

🌐 Asynchronous functions use the `async` keyword to `await` tasks such as API calls or database queries before Strapi continues.

```js
module.exports = {
  async register({ strapi }) {
    await new Promise((resolve) => setTimeout(resolve, 200));
    strapi.log.info('Async register finished after a short delay');
  },
  async bootstrap({ strapi }) {
    const { results: articles } = await strapi
      .documents('api::article.article')
      .findMany({
        filters: { publishedAt: { $notNull: true } },
        fields: ['id'],
      });
    strapi.log.info(`Indexed ${articles.length} published articles`);
  },
  async destroy({ strapi }) {
    await strapi.documents('api::temporary-cache.temporary-cache').deleteMany({
      filters: {},
    });
  }
};
```

```ts

  async register({ strapi }) {
    await new Promise((resolve) => setTimeout(resolve, 200));
    strapi.log.info('Async register finished after a short delay');
  },
  async bootstrap({ strapi }) {
    const { results: articles } = await strapi
      .documents('api::article.article')
      .findMany({
        filters: { publishedAt: { $notNull: true } },
        fields: ['id'],
      });
    strapi.log.info(`Indexed ${articles.length} published articles`);
  },
  async destroy({ strapi }) {
    await strapi.documents('api::temporary-cache.temporary-cache').deleteMany({
      filters: {},
    });
  }
};
```

### 返回一个承诺 {#returning-a-promise}

🌐 Returning a promise

返回 Promise 的函数会返回一个 Promise，以便 Strapi 可以等待其解析后再继续执行。

🌐 Promise-returning functions hand back a promise so Strapi can wait for its resolution before continuing.

```js
module.exports = {
  register({ strapi }) {
    return new Promise((resolve) => {
      strapi.log.info('Registering with a delayed startup task');
      setTimeout(resolve, 200);
    });
  },
  bootstrap({ strapi }) {
    return new Promise((resolve, reject) => {
      strapi
        .documents('api::category.category')
        .findMany({ filters: { slug: 'general' }, pageSize: 1 })
        .then(({ results }) => {
          if (results.length === 0) {
            return strapi.documents('api::category.category').create({
              data: { name: 'General', slug: 'general' },
            });
          }

          return results[0];
        })
        .then(() => {
          strapi.log.info('Ensured default category exists');
          resolve();
        })
        .catch(reject);
    });
  },
  destroy({ strapi }) {
    return new Promise((resolve, reject) => {
      strapi
        .documents('api::temporary-cache.temporary-cache')
        .deleteMany({ filters: {} })
        .then(() => {
          strapi.log.info('Cleared temporary cache before shutdown');
          resolve();
        })
        .catch(reject);
    });
  }
};
```

```ts

  register({ strapi }) {
    return new Promise((resolve) => {
      strapi.log.info('Registering with a delayed startup task');
      setTimeout(resolve, 200);
    });
  },
  bootstrap({ strapi }) {
    return new Promise((resolve, reject) => {
      strapi
        .documents('api::category.category')
        .findMany({ filters: { slug: 'general' }, pageSize: 1 })
        .then(({ results }) => {
          if (results.length === 0) {
            return strapi.documents('api::category.category').create({
              data: { name: 'General', slug: 'general' },
            });
          }

          return results[0];
        })
        .then(() => {
          strapi.log.info('Ensured default category exists');
          resolve();
        })
        .catch(reject);
    });
  },
  destroy({ strapi }) {
    return new Promise((resolve, reject) => {
      strapi
        .documents('api::temporary-cache.temporary-cache')
        .deleteMany({ filters: {} })
        .then(() => {
          strapi.log.info('Cleared temporary cache before shutdown');
          resolve();
        })
        .catch(reject);
    });
  }
};
```

## 生命周期函数 {#lifecycle-functions}

🌐 Lifecycle functions

生命周期函数允许你在 Strapi 启动和关闭的特定阶段放置代码。

🌐 Lifecycle functions let you place code at specific phases of Strapi's startup and shutdown.

- `register()` 函数用于服务启动前的配置时设置。
- `bootstrap()` 函数用于需要 Strapi API 的初始化。
- `destroy()` 函数用于在应用停止时进行清理。

### 注册 {#register}

🌐 Register

`register` 生命周期函数，位于 `./src/index.js`（或在 `./src/index.ts` 中），是一个在应用初始化之前运行的异步函数。

🌐 The `register` lifecycle function, found in `./src/index.js` (or in `./src/index.ts`), is an asynchronous function that runs before the application is initialized.

`register()` 是 Strapi 应用启动时发生的第一件事。这发生在任何设置过程之前，并且在 `register()` 函数中你无法访问数据库、路由、策略或任何其他后端服务器元素。

`register()` 函数可以用于：

🌐 The `register()` function can be used to:

- [扩展插件](/cms/plugins-development/plugins-extension#extending-a-plugins-interface)
- 以编程方式扩展 [content-types](/cms/backend-customization/models)
- 加载一些 [环境变量](/cms/configurations/environment)
- 注册一个[自定义字段](/cms/features/custom-fields)，该字段仅供当前 Strapi 应用使用，
- 注册一个 [自定义提供程序用于 Users & Permissions 插件](/cms/configurations/users-and-permissions-providers/new-provider-guide)。

更具体地说，`register()` 的典型用例包括前置安全任务，例如在应用完成初始化之前加载密钥、轮换 API 密钥或注册认证提供者。

🌐 More specifically, typical use-cases for `register()` include front-load security tasks such as loading secrets, rotating API keys, or registering authentication providers before the app finishes initializing.

```js
module.exports = {
  register({ strapi }) {
    strapi.customFields.register({
      name: 'color',
      plugin: 'my-color-picker',
      type: 'string',
    });
  },
};
```

```ts

  register({ strapi }) {
    strapi.customFields.register({
      name: 'color',
      plugin: 'my-color-picker',
      type: 'string',
    });
  },
};
```

### 自举 {#bootstrap}

🌐 Bootstrap

`bootstrap` 生命周期函数，位于 `./src/index.js`（或 `./src/index.ts` 中），在每次服务器启动时都会被调用。

🌐 The `bootstrap` lifecycle function, found in `./src/index.js` (or in `./src/index.ts`), is called at every server start.

`bootstrap()` 会在后端服务器启动之前但在 Strapi 应用设置之后运行，因此你可以访问 `strapi` 对象中的任何内容。

`bootstrap` 函数可以用于：

🌐 The `bootstrap` function can be used to:

- 如果没有管理员用户，请创建一个
- 用一些必要的数据填充数据库
- 为 [基于角色的访问控制 (RBAC)](/cms/configurations/guides/rbac) 功能声明自定义条件

更具体地说，`bootstrap()` 的典型用例是支持编辑工作流程。例如，通过提供初始内容、附加 webhook，或在启动时安排 cron 作业。

🌐 More specifically, a typical use-case for `bootstrap()` is supporting editorial workflows. For example by seeding starter content, attaching webhooks, or scheduling cron jobs at startup.

:::tip

你可以在终端中运行 `yarn strapi console`（或 `npm run strapi console`）并与 `strapi` 对象进行交互。

🌐 You can run `yarn strapi console` (or `npm run strapi console`) in the terminal and interact with the `strapi` object.

:::

```js
module.exports = {
  async bootstrap({ strapi }) {
    const { results } = await strapi
      .documents('api::category.category')
      .findMany({ filters: { slug: 'general' }, pageSize: 1 });

    if (results.length === 0) {
      await strapi.documents('api::category.category').create({
        data: { name: 'General', slug: 'general' },
      });
      strapi.log.info('Created default category');
    }
  },
};
```

```ts

  async bootstrap({ strapi }) {
    const { results } = await strapi
      .documents('api::category.category')
      .findMany({ filters: { slug: 'general' }, pageSize: 1 });

    if (results.length === 0) {
      await strapi.documents('api::category.category').create({
        data: { name: 'General', slug: 'general' },
      });
      strapi.log.info('Created default category');
    }
  },
};
```

### 摧毁 {#destroy}

🌐 Destroy

`./src/index.js`（或`./src/index.ts`）中的`destroy`函数是一个异步函数，会在应用关闭之前运行。

🌐 The `destroy` function, found in `./src/index.js` (or in `./src/index.ts`), is an asynchronous function that runs before the application gets shut down.

`destroy` 函数可用于优雅地：

🌐 The `destroy` function can be used to gracefully:

- 停止正在运行的[服务](/cms/backend-customization/services)
- [清理插件操作](/cms/plugins-development/server-lifecycle#destroy)（例如关闭连接、移除监听器等）

更具体地说，`destroy()` 的一个典型用例是处理操作清理，例如关闭数据库或队列连接以及移除监听器，以便应用能够干净地关闭。

🌐 More specifically, a typical use-case for `destroy()` is to handle operational clean-up, such as closing database or queue connections and removing listeners so the application can shut down cleanly.

```js
let heartbeat;

module.exports = {
  async bootstrap({ strapi }) {
    heartbeat = setInterval(() => {
      strapi.log.debug('Heartbeat interval running');
    }, 60_000);
  },

  async destroy() {
    clearInterval(heartbeat);
  },
};
```

```ts
let heartbeat: ReturnType<typeof setInterval>;

  async bootstrap({ strapi }) {
    heartbeat = setInterval(() => {
      strapi.log.debug('Heartbeat interval running');
    }, 60_000);
  },

  async destroy() {
    clearInterval(heartbeat);
  },
};
```

## 使用 {#usage}

🌐 Usage

<br/>

### 组合使用 {#combined-usage}

🌐 Combined usage

所有 3 个生命周期函数可以组合使用，以配置应用启动和关闭期间的自定义行为。

🌐 All 3 lifecycle functions can be put together to configure custom behavior during application startup and shutdown.

1. 确定逻辑的运行时间。
   - 在 `register()` 中添加仅初始化任务（例如，注册自定义字段或提供程序）。
   - 在 `bootstrap()` 中添加需要完整 Strapi 访问权限的启动任务（例如，播种或附加网页钩子）。
   - 在 `destroy()` 中添加清理逻辑（例如关闭外部连接）。
2. 将代码放入 `src/index.js|ts`。保持 `register()` 精简，因为它在 Strapi 完全设置之前运行。
3. 重启 Strapi 以确认每个生命周期按顺序执行。

```ts title="src/index.ts"
let cronJobKey: string | undefined;

  register({ strapi }) {
    strapi.customFields.register({
      name: 'color',
      type: 'string',
      plugin: 'color-picker',
    });
  },

  async bootstrap({ strapi }) {
    cronJobKey = 'log-reminders';

    strapi.cron.add({
      [cronJobKey]: {
        rule: '0 */6 * * *', // every 6 hours
        job: async () => {
          strapi.log.info('Remember to review new content in the admin panel.');
        },
      },
    });
  },

  async destroy({ strapi }) {
    if (cronJobKey) {
      strapi.cron.remove(cronJobKey);
    }
  },
};
```

:::strapi Additional information

你可能会在 [this blog article](https://strapi.io/blog/how-to-use-register-function-to-customize-your-strapi-app) 中找到关于注册生命周期函数的更多信息。

:::
