构建你自己的插件
构建你自己的 Payload 插件 非常简单,如果你已经熟悉 Payload,那么你就已经具备了开始所需的一切。你可以从头开始构建,或者使用 插件模板 来快速启动和运行。
要使用模板,直接在终端中运行 npx create-payload-app@latest --template plugin
。
我们的插件模板包含构建完整生命周期插件所需的一切:
- 用于扩展 Payload Config 的示例文件和函数
- 用于开发插件的本地开发环境
- 集成了 GitHub workflow 的测试套件
通过将你的代码抽象成插件,你将能够在多个项目中重用你的功能,并使其可供其他开发者使用。
插件回顾
以下是关于如何与 Payload 集成插件的简要回顾,要了解更多信息,请返回 插件概述页面。
如何安装插件
要安装任何插件,只需将其添加到 Payload Config 的 plugins 数组中。
import samplePlugin from 'sample-plugin';
const config = buildConfig({
plugins: [
// 在此处添加插件
samplePlugin({
enabled: true,
}),
],
});
export default config;
初始化流程
初始化流程按以下顺序进行:
- 验证传入的配置
- 执行插件
- 集成默认选项
- 清理和验证数据
- 初始化最终配置
插件模板
在 Payload 插件模板 中,你会看到一个跨插件使用的通用文件结构:
/
根文件夹 - 通用配置/src
文件夹 - 与插件相关的所有内容/dev
文件夹 - 用于开发的经过清理的测试项目
根目录
在根目录中,你会看到与插件配置相关的各种文件。我们在 Payload 核心和其他项目中都采用了类似的环境设置方式。你只需要修改以下两个文件:
- README.md - 包含如何使用该模板的说明。当你准备好时,更新此文件以包含关于如何使用你的插件的说明。
- package.json - 包含必要的脚本和依赖项。覆盖此文件中的元数据以描述你的插件。
dev 文件夹
dev 文件夹的目的是提供一个干净的本地 Payload 项目环境,以便你在开发插件时能够运行和测试它。
请不要将任何插件功能存储在此文件夹中 - 它纯粹是一个用于_辅助_你开发插件的环境。
如果你从零开始,可以像这样轻松设置开发环境:
mkdir dev
cd dev
npx create-payload-app@latest
如果你使用插件模板,dev 文件夹已经为你构建好,并且 samplePlugin
已经安装在 dev/payload.config.ts
中:
plugins: [
// 当你重命名插件或添加选项时,确保在此处更新
samplePlugin({
enabled: false,
})
]
你可以根据需要修改 dev/payload.config.ts
并构建开发项目来测试你的插件。
当你准备开始开发时,使用 cd dev
进入此文件夹。
然后使用 pnpm dev
启动项目,并在浏览器中打开 http://localhost:3000
。
测试
dev 文件夹的另一个优势是它为你建立了一个完美的测试环境。
一套良好的测试套件对于确保插件质量和稳定性至关重要。Payload 通常使用 Jest —— 一个流行的测试框架,广泛用于测试 JavaScript 应用,特别是基于 React 构建的应用。
Jest 将测试组织为测试套件和测试用例。我们建议根据插件从始至终的预期行为来创建测试。更多关于测试的信息请参阅 Jest 文档。
插件模板在 dev/plugin.spec.ts
提供了一个现成的测试套件框架 —— 只需添加你自己的测试条件即可开始使用!
let payload: Payload
describe('Plugin tests', () => {
// 检查种子数据的示例测试
it('seeds data accordingly', async () => {
const newCollectionQuery = await payload.find({
collection: 'newCollection',
sort: 'createdAt',
})
newCollection = newCollectionQuery.docs
expect(newCollectionQuery.totalDocs).toEqual(1)
})
})
数据播种
在开发和测试过程中,你可能需要一些数据来开展工作。通过播种和清空数据库可以简化这一过程 —— 而不需要手动输入数据。
在插件模板中,你可以导航到 dev/src/server.ts
查看一个种子函数的示例。
if (process.env.PAYLOAD_SEED === 'true') {
await seed(payload)
}
模板已在 dev/src/seed
为你创建了一个示例种子函数,根据需要更新此文件以添加更多数据。
export const seed = async (payload: Payload): Promise<void> => {
payload.logger.info('正在播种数据...')
await payload.create({
collection: 'new-collection',
data: {
title: '种子标题',
},
})
// 在此处添加额外的种子数据
}
构建插件
现在我们已经设置好环境并准备好开发项目 - 是时候构建插件了!
import type { Config } from 'payload'
export const samplePlugin =
(pluginOptions: PluginTypes) =>
(incomingConfig: Config): Config => {
// 创建传入配置的副本
let config = { ...incomingConfig }
/**
* 在这里你可以根据插件选项
* 修改配置
*/
// 如果你想添加一个新的 collection:
config.collections = [
...(config.collections || []),
newCollection,
]
// 如果你想添加一个新的 global:
config.globals = [
...(config.globals || []),
newGlobal,
]
/**
* 如果你想向 collection 添加新字段:
*
* 1. 遍历 collections
* 2. 找到要添加字段的 collection
* 3. 向该 collection 添加字段
*/
// 如果你想添加到 onInit:
config.onInit = async payload => {
if (incomingConfig.onInit) await incomingConfig.onInit(payload)
// 在这里添加额外的 onInit 代码
}
// 最后,返回修改后的配置
return config
}
再次强调,Payload 插件的本质就是扩展 Payload 配置 - 这正是我们在这个文件中所做的。
展开语法
展开语法(或称展开操作符)是 JavaScript 的一项特性,使用点号表示法 (...) 将数组、字符串或对象的元素展开到不同上下文中。
我们将使用展开语法来实现在不丢失现有数据的情况下向数组添加新数据。正确展开现有数据至关重要,否则可能导致异常行为并与 Payload 配置和其他插件产生冲突。
假设你想构建一个添加新 collection 的插件:
config.collections = [
...(config.collections || []),
newCollection,
// 在此处添加其他 collections
]
首先,你需要展开 config.collections
以确保不丢失现有的 collections。然后你可以像在常规 Payload 配置中一样添加其他 collections。
同样的逻辑适用于其他数组和对象类型的属性,如 admin、globals 和 hooks:
config.globals = [
...(config.globals || []),
// 在此处添加其他 globals
]
config.hooks = {
...(config.hooks || {}),
// 在此处添加其他 hooks
}
扩展函数
函数属性不能使用展开语法。扩展它们的方法是先执行现有函数(如果存在),然后再运行你的附加功能。
以下是扩展 onInit
属性的示例:
config.onInit = async payload => {
if (incomingConfig.onInit) await incomingConfig.onInit(payload)
// 通过使用 onInitExtension 函数添加额外的 onInit 代码
onInitExtension(pluginOptions, payload)
}
类型定义
如果你的插件包含配置选项,应该在单独的 index.ts
文件中定义并导出这些选项的类型。
export interface PluginTypes {
/**
* 启用或禁用插件
* @default false
*/
enabled?: boolean
}
尽可能使用 JSDoc 注释来描述选项及其类型。这样开发者就能在他们的编辑器中看到选项的详细信息。
最佳实践
除了上述设置外,以下是其他值得遵循的最佳实践:
提供启用/禁用选项
为了更好的用户体验,提供一种无需卸载即可禁用插件的方式。
在 GitHub CI 工作流中包含测试
如果你为包配置了测试,将它们集成到工作流中,以便每次提交到插件仓库时自动运行测试。了解更多关于如何在 GitHub CI 工作流中配置测试。
将完成的插件发布到 npm
插件开发完成后,分享并允许他人使用的最佳方式是发布 npm 包。这个过程简单且文档完善,了解更多关于创建和发布 npm 包的信息。
添加 payload-plugin 主题标签
在你的 GitHub 仓库上添加 payload-plugin 标签。这将提高插件的可见性,并确保它被列在现有的 Payload 插件中。
使用语义化版本控制 (SemVer)
通过语义化版本控制 (SemVer) 系统,你可以发布反映变更性质(主版本号、次版本号、修订号)的版本号。确保所有主版本都注明其兼容的 Payload 版本。