管理面板 API:钩子
🌐 Admin Panel API: Hooks
Page summary:
Hooks API 允许插件创建扩展点(
createHook在register中)并订阅它们(registerHook在bootstrap中)。Hooks 可以串行、瀑布式或并行运行。Strapi 为内容管理器的列表和编辑视图包含了预定义的 hooks。🌐 The Hooks API lets plugins create extension points (
createHookinregister) and subscribe to them (registerHookinbootstrap). Hooks run in series, waterfall, or parallel. Strapi includes predefined hooks for the Content Manager's List and Edit views.
Hooks API 允许插件创建和注册钩子,即应用中插件可以添加个性化行为的位置。
🌐 The Hooks API allows a plugin to create and register hooks, i.e. places in the application where plugins can add personalized behavior.
在深入了解本页的概念之前,请确保你已经:
🌐 Before diving deeper into the concepts on this page, please ensure you have:
- 创建了一个 Strapi 插件,
- 已阅读并理解了 管理面板 API 的基础知识
创建钩子
🌐 Creating hooks
在register生命周期期间使用createHook()创建钩子扩展点。这表明你的插件提供了一个其他插件可以订阅的扩展点。
🌐 Create hook extension points with createHook() during the register lifecycle. This declares that your plugin provides an extension point that other plugins can subscribe to.
- JavaScript
- TypeScript
export default {
register(app) {
app.createHook('My-PLUGIN/MY_HOOK');
},
};
import type { StrapiApp } from '@strapi/admin/strapi-admin';
export default {
register(app: StrapiApp) {
app.createHook('My-PLUGIN/MY_HOOK');
},
};
为了插件之间可预测的互操作性,请使用稳定的命名空间钩子 ID,例如 my-plugin/my-hook。
🌐 For predictable interoperability between plugins, use stable namespaced hook IDs such as my-plugin/my-hook.
订阅钩子
🌐 Subscribing to hooks
在所有插件加载完成后,在bootstrap生命周期中使用registerHook()订阅钩子。回调函数会接收来自钩子调用者的参数,并应返回(可选修改的)数据。
🌐 Subscribe to hooks with registerHook() during the bootstrap lifecycle, once all plugins are loaded. The callback receives arguments from the hook caller and should return the (optionally mutated) data.
- JavaScript
- TypeScript
export default {
bootstrap(app) {
app.registerHook('My-PLUGIN/MY_HOOK', (...args) => {
console.log(args);
// important: return the mutated data
return args;
});
},
};
import type { StrapiApp } from '@strapi/admin/strapi-admin';
export default {
bootstrap(app: StrapiApp) {
app.registerHook('My-PLUGIN/MY_HOOK', (...args: unknown[]) => {
console.log(args);
// important: return the mutated data
return args;
});
},
};
异步回调也被支持:
🌐 Async callbacks are also supported:
- JavaScript
- TypeScript
export default {
bootstrap(app) {
app.registerHook('My-PLUGIN/MY_HOOK', async (data) => {
const enrichedData = await fetchExternalData(data);
// always return data for waterfall hooks
return enrichedData;
});
},
};
import type { StrapiApp } from '@strapi/admin/strapi-admin';
export default {
bootstrap(app: StrapiApp) {
app.registerHook('My-PLUGIN/MY_HOOK', async (data: unknown) => {
const enrichedData = await fetchExternalData(data);
// always return data for waterfall hooks
return enrichedData;
});
},
};
运行钩子
🌐 Running hooks
钩子可以在三种模式下运行:
🌐 Hooks can be run in 3 modes:
| 模式 | 功能 | 返回值 ||---|---|---|| 串行 | runHookSeries | 每个函数的结果数组,按顺序 || 并行 | runHookParallel | 已解决的 Promise 结果数组,按顺序 || 瀑布 | runHookWaterfall | 应用所有转换后得到的单个值 |
对于 runHookWaterfall,每个订阅者必须返回转换后的值,以便链中的下一个订阅者接收它。不返回值将会打断链。
🌐 For runHookWaterfall, each subscriber must return the transformed value so that the next subscriber in the chain receives it. Failing to return a value will break the chain.
使用预定义的钩子
🌐 Using predefined hooks
Strapi 包含为内容管理器的列表和编辑视图预定义的钩子。
🌐 Strapi includes predefined hooks for the Content Manager's List and Edit views.
INJECT-COLUMN-IN-TABLE
Admin/CM/pages/ListView/inject-column-in-table 钩子可以在 内容管理器 的列表视图中添加或修改列:
🌐 The Admin/CM/pages/ListView/inject-column-in-table hook can add or mutate columns in the List View of the Content Manager:
runHookWaterfall(INJECT_COLUMN_IN_TABLE, {
displayedHeaders: ListFieldLayout[],
layout: ListFieldLayout,
});
以下示例订阅此钩子以添加自定义“外部 ID”列:
🌐 The following example subscribes to this hook to add a custom "External id" column:
- JavaScript
- TypeScript
export default {
bootstrap(app) {
app.registerHook(
'Admin/CM/pages/ListView/inject-column-in-table',
({ displayedHeaders, layout }) => {
return {
displayedHeaders: [
...displayedHeaders,
{
attribute: { type: 'custom' },
label: 'External id',
name: 'externalId',
searchable: false,
sortable: false,
cellFormatter: (document) => document.externalId,
},
],
layout,
};
}
);
},
};
import type { StrapiApp } from '@strapi/admin/strapi-admin';
export default {
bootstrap(app: StrapiApp) {
app.registerHook(
'Admin/CM/pages/ListView/inject-column-in-table',
({ displayedHeaders, layout }) => {
return {
displayedHeaders: [
...displayedHeaders,
{
attribute: { type: 'custom' },
label: 'External id',
name: 'externalId',
searchable: false,
sortable: false,
cellFormatter: (document) => document.externalId,
},
],
layout,
};
}
);
},
};
ListFieldLayout 和 ListLayout 类型定义:
MUTATE-EDIT-VIEW-LAYOUT
Admin/CM/pages/EditView/mutate-edit-view-layout 钩子可以修改 内容管理器 的编辑视图布局。
🌐 The Admin/CM/pages/EditView/mutate-edit-view-layout hook can mutate the Edit View layout of the Content Manager.
下面的示 例订阅这个钩子以将所有字段强制为全宽:
🌐 The following example subscribes to this hook to force all fields to full width:
- JavaScript
- TypeScript
export default {
bootstrap(app) {
app.registerHook(
'Admin/CM/pages/EditView/mutate-edit-view-layout',
({ layout, ...rest }) => {
// Force all fields to full width in the default edit layout
const updatedLayout = layout.map((rowGroup) =>
rowGroup.map((row) => row.map((field) => ({ ...field, size: 12 })))
);
return {
...rest,
layout: updatedLayout,
};
}
);
},
};
import type { StrapiApp } from '@strapi/admin/strapi-admin';
export default {
bootstrap(app: StrapiApp) {
app.registerHook(
'Admin/CM/pages/EditView/mutate-edit-view-layout',
({ layout, ...rest }) => {
// Force all fields to full width in the default edit layout
const updatedLayout = layout.map((rowGroup) =>
rowGroup.map((row) => row.map((field) => ({ ...field, size: 12 })))
);
return {
...rest,
layout: updatedLayout,
};
}
);
},
};
EditLayout 和 EditFieldLayout 类型定义:
此处记录的 EditLayout 和 ListLayout 形状来自 useDocumentLayout 钩子(参见 source code)。内部包命名可能有所不同,但插件作者应依赖本页中公开的 EditLayout 和 ListLayout 形状。