用途
当你在编写一个 Backstage 前端插件,并希望让其他插件或模块以带类型、结构化的方式向你的插件贡献 UI 时,使用本技能创建一个新的扩展点(blueprint)。这与 Backstage 自身使用的 PageBlueprint、EntityCardBlueprint、EntityContentBlueprint、FormFieldBlueprint、ApiBlueprint 属于同一模式,你是在为自己的插件创建这类 blueprint 中的一个。
典型场景包括:
- 一个仪表盘插件,希望模块注册自定义仪表盘面板;
- 一个代码质量插件,希望模块注册显示在结果页上的额外检查项;
- 一个可复用的“功能开关”插件,模块按开关贡献 UI 控件。
何时不要创建 blueprint
- 已有内置 blueprint 覆盖该场景(例如目录页卡片用 EntityCardBlueprint,scaffolder 字段用 FormFieldBlueprint);
- 你的扩展点只会触发一次,且只有你自己的插件会填充它——直接写一个普通扩展,跳过 blueprint 这层间接;
- 你在后端插件中——后端使用扩展点(createExtensionPoint),而非 blueprint。参见 using-backstage-backend-system/references/extension-points.md。
需要构建的内容
一个 blueprint 由两个相互耦合的部分组成:
- blueprint 本身(createExtensionBlueprint)——其他插件调用的带类型工厂,形式为 MyBlueprint.make({ name, params }),用于贡献单个实例。
- 一个父扩展,声明一个 input,并渲染或使用所有附加到它上面的内容。这通常是你在插件 extensions.tsx 中定义的 PageBlueprint 扩展。
两部分都是必需的,且必须在 input 名称以及贡献所发出的 data refs 上保持一致。多数插件会在同一个 extensions.tsx(或同目录模块)中编写这两部分。
选择正确的模式
| 正在构建…… | 参考文档 |
|---|---|
| blueprint 本身(createExtensionBlueprint 定义) | references/defining-the-blueprint.md |
| 通过 inputs 收集贡献的父扩展 | references/parent-collecting-extensions.md |
两种调用模式
- 独立模式:编辑一个已有的前端插件,添加新 blueprint 或改进已有 blueprint。
- 脚手架后模式(由 creating-backstage-plugin 针对 frontend-plugin 调用):较少见——仅当用户意图明确描述“我希望我的插件可被其他模块扩展”时使用。多数插件不需要,也不应具备此能力。
常见注意事项
- Blueprint 位于前端。后端对应物是 createExtensionPoint——API 不同、包不同。若你既想要可扩展 UI 又想要可扩展后端行为,需分别创建两者,使用不同的符号和文档。
- blueprint 的 attachTo 必须指向同一插件 extensions 数组中某个真实扩展上声明的 input。id/input 字符串不匹配会导致静默的“无贡献”——blueprint 能编译,但不会有任何东西附加。
- 谨慎选择 output——这是每个贡献都必须产出的 data refs。如果 blueprint 要求 coreExtensionData.reactElement 加上一个自定义 data ref,那么每个使用方的工厂都必须同时产出两者。之后添加必需 ref 对使用方是破坏性变更。
- 使用 dataRefs 提供可选的、具名访问器,供父扩展从贡献上读取(例如“贡献可选地提供一个排序键,通过 MyBlueprint.dataRefs.sortKey 访问”)。必需输出放在 output 中;可选/贡献类型数据放在 dataRefs 中。
- configSchema 让采用者无需改代码即可通过 app-config 覆盖每个贡献的设置。仅当贡献有面向用户的设置(标题、路径、过滤器)时才定义;否则跳过。
- 工厂是一个生成器——function* (...) 产出 ref(value) 调用。每次 yield 添加一份扩展数据。循环、条件判断以及看似异步的工作都在工厂内同步发生;真正异步的工作应放在 params 传入的 loader 回调中。
- 两阶段 params(createExtensionBlueprintParams)——对于 params 需要函数形式的 blueprint(例如 { field: () => Promise<...> }),用 createExtensionBlueprintParams 包装,以便使用方获得类型推断。真实示例见 ../backstage/plugins/scaffolder-react/src/next/blueprints/FormFieldBlueprint.tsx。
- make 与 makeWithOverrides:使用方通常调用 MyBlueprint.make({ name, params })。makeWithOverrides 变体让更高级的使用方覆盖 factory 以包装原始实现——用于在 blueprint 之上添加 inputs 的场景。在 README 中记录 make;仅在高级场景提及 makeWithOverrides。