函数

页面摘要: src/index 包含全局的 register、bootstrap 和 destroy 函数,用于在应用程序生命周期内运行逻辑。

./src/index.js 文件(在基于 TypeScript的项目中为 ./src/index.ts 文件)包含全局的 register、bootstrap 和 destroy 函数,可用于添加动态且基于逻辑的配置。

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

生命周期函数示意图

可用模式

生命周期函数支持 3 种执行模式/方式,以便你将其与它们所管理的依赖项对齐。Strapi 会等待每个函数完成——无论是正常返回、解析一个 async 函数,还是解析一个 Promise——然后才会继续启动或关闭。

返回值不会被 Strapi 使用,因此函数应在其设置或清理完成时解析(或返回),并通过抛出错误或拒绝来表明失败。

同步

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

JavaScript

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');
  }
};

TypeScript

export default {
  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');
  }
};

异步

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

JavaScript

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 articles = await strapi
      .documents('api::article.article')
      .findMany({
        status: 'published',
        fields: ['documentId'],
      });
    strapi.log.info(`Indexed ${articles.length} published articles`);
  },
  async destroy({ strapi }) {
    const cachedEntries = await strapi
      .documents('api::temporary-cache.temporary-cache')
      .findMany({ fields: ['documentId'] });

    for (const { documentId } of cachedEntries) {
      await strapi
        .documents('api::temporary-cache.temporary-cache')
        .delete({ documentId });
    }
  }
};

TypeScript

export default {
  async register({ strapi }) {
    await new Promise((resolve) => setTimeout(resolve, 200));
    strapi.log.info('Async register finished after a short delay');
  },
  async bootstrap({ strapi }) {
    const articles = await strapi
      .documents('api::article.article')
      .findMany({
        status: 'published',
        fields: ['documentId'],
      });
    strapi.log.info(`Indexed ${articles.length} published articles`);
  },
  async destroy({ strapi }) {
    const cachedEntries = await strapi
      .documents('api::temporary-cache.temporary-cache')
      .findMany({ fields: ['documentId'] });

    for (const { documentId } of cachedEntries) {
      await strapi
        .documents('api::temporary-cache.temporary-cache')
        .delete({ documentId });
    }
  }
};

返回一个 Promise

返回 Promise 的函数会交还一个 Promise,以便 Strapi 可以等待其解析后再继续。

JavaScript

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((categories) => {
          if (categories.length === 0) {
            return strapi.documents('api::category.category').create({
              data: { name: 'General', slug: 'general' },
            });
          }

          return categories[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')
        .findMany({ fields: ['documentId'] })
        .then((cachedEntries) =>
          Promise.all(
            cachedEntries.map(({ documentId }) =>
              strapi
                .documents('api::temporary-cache.temporary-cache')
                .delete({ documentId })
            )
          )
        )
        .then(() => {
          strapi.log.info('Cleared temporary cache before shutdown');
          resolve();
        })
        .catch(reject);
    });
  }
};

TypeScript

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

          return categories[0];
        })
        .then(() => {
          strapi.log.info('Ensured default category exists');
          resolve();
        })
        .catch(reject);
    });
  },
  destroy({ strapi }) {
    return new Promise<void>((resolve, reject) => {
      strapi
        .documents('api::temporary-cache.temporary-cache')
        .findMany({ fields: ['documentId'] })
        .then((cachedEntries) =>
          Promise.all(
            cachedEntries.map(({ documentId }) =>
              strapi
                .documents('api::temporary-cache.temporary-cache')
                .delete({ documentId })
            )
          )
        )
        .then(() => {
          strapi.log.info('Cleared temporary cache before shutdown');
          resolve();
        })
        .catch(reject);
    });
  }
};

生命周期函数

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

  • register() 函数用于服务启动前的配置期设置。
  • bootstrap() 函数用于需要 Strapi API 的初始化。
  • destroy() 函数用于应用程序停止时的拆卸。

Register

register 生命周期函数位于 ./src/index.js(或 ./src/index.ts)中,是一个在应用程序初始化之前运行的异步函数。

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

register() 函数可用于:

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

JavaScript

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

TypeScript

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

Bootstrap

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

bootstrap() 在后端服务器启动_之前_、但在 Strapi 应用程序完成设置_之后_运行,因此你可以访问 strapi 对象中的任何内容。

bootstrap 函数可用于:

更具体地说,bootstrap() 的一个典型用例是支持编辑工作流。例如在启动时植入起始内容、附加 Webhook 或调度定时任务。

TIP

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

JavaScript

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

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

TypeScript

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

    if (categories.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 函数可用于优雅地:

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

JavaScript

let heartbeat;

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

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

TypeScript

let heartbeat: ReturnType<typeof setInterval>;

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

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

用法

组合使用

全部 3 个生命周期函数可以放在一起,用于在应用程序启动和关闭期间配置自定义行为。

  1. 决定你的逻辑应该何时运行。
    • 将仅初始化的任务(例如注册自定义字段或提供方)放在 register() 中。
    • 将需要完整 Strapi 访问权限的启动任务(例如植入数据或附加 Webhook)放在 bootstrap() 中。
    • 将清理逻辑(例如关闭外部连接)放在 destroy() 中。
  2. 将代码放在 src/index.js|ts 中。保持 register() 精简,因为它在 Strapi 完全设置之前运行。
  3. 重新启动 Strapi 以确认每个生命周期按顺序执行。
let cronJobKey: string | undefined;

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

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

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

  async destroy({ strapi }) {
    if (cronJobKey) {
      strapi.cron.remove(cronJobKey);
    }
  },
};
附加信息

你可以在 这篇博客文章 中找到关于注册生命周期函数的附加信息。