
Backstage 插件与 Software Catalog 集成指南从 useEntity 到 EntitySwitch/EntityLayout 完整实战【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage导读本文介绍如何将一个独立的前端插件集成进 Backstage 的 Software Catalog软件目录实体页面使你开发的插件能够在浏览某个组件Component、系统System、API 等实体时展示与该实体相关的额外信息。文章以docs/plugins/integrating-plugin-into-software-catalog.md为主线结合仓库内backstage/plugin-catalog-react与backstage/plugin-catalog的源码实现完整讲解插件的创建、通过useEntity读取当前实体、以及在EntityPage.tsx中使用EntitySwitch与EntityLayout嵌入自定义内容的全部步骤。读完本文你将掌握把任意插件页面挂载到实体详情页 Tab 上的完整方法并理解其底层 Context 传递机制与条件渲染原理。注意本文介绍的是基于旧版前端系统的实体页集成方式EntitySwitch、EntityLayout、EntityLayout.Route。若使用新版前端系统实体页集成应改用EntityContentBlueprint与EntityCardBlueprint详见 Common Extension Blueprints。此外本能力属于高级用例且目前为实验性功能API 后续可能会发生变化。一、整体流程概览将一个插件集成进 Software Catalog 实体页核心只有三步创建一个插件与创建独立插件完全相同的流程得到一个独立的前端插件包在插件内读取实体通过backstage/plugin-catalog-react提供的useEntityHook 获取当前正在浏览的实体导入插件并嵌入实体页在应用根目录的EntityPage.tsx中引入插件组件用EntitySwitch按实体 kind 路由、用EntityLayout.Route增加新的页面 Tab。下面逐一展开。二、创建插件与独立插件同一套流程插件集成 Catalog 的前提是它首先是一个正常的 Backstage 前端插件。创建流程与独立插件完全一致可参考 创建插件文档在项目根目录执行yarn new选择frontend-plugin然后按提示输入插件 ID 与可选的 owner 信息$ yarn new # Select frontend-plugin ? Enter an ID for the plugin [required] my-plugin ? Enter the owner(s) of the plugin. If specified, this will be added to CODEOWNERS for the plugin path. [optional] Creating the plugin...yarn new是backstage-cli new --select plugin的快捷方式见 cli 模块 new 命令文档。创建完成后插件会作为一个独立包出现在plugins/my-plugin目录下并自动被构建、接入 Backstage App。若 App 正在运行yarn start可以直接访问http://localhost:3000/my-plugin看到插件的默认页面也可以在插件目录内运行yarn start或yarn workspace backstage/plugin-my-plugin start在隔离环境中开发以获得更快的启动与热重载体验——该方式的配置位于插件内的dev/目录仅用于本地开发。从仓库结构看插件包通常同时包含前端插件源码与自身依赖声明如 插件 catalog-react 本身即是这样一个插件包。一个需要嵌入实体页的插件通常只需新增一个实体内容组件Entity Content Component再由 App 侧的实体页引用它。三、读取当前实体useEntity 与背后的 Context 机制实体页最大的价值在于上下文相关当用户正浏览某个实体时插件应能拿到这个实体本身。Backstage 为此提供了backstage/plugin-catalog-react中的useEntityHookimport { useEntity } from backstage/plugin-catalog-react; export const MyPluginEntityContent () { const entity useEntity(); // Do something with the entity data... // 例如读取 entity.metadata.name、entity.kind、entity.spec 等字段 };3.1 useEntity 的实现原理useEntity本质上依赖 React 的Context机制来向下传递实体数据实体 Context 由插件将要嵌入的实体页提供。查看 useEntity.tsx 的实现可以发现其实体上下文是通过backstage/version-bridge的createVersionedContext创建的版本化 Contextkey 为entity-context以此支持同一页面中存在多个版本的backstage/plugin-catalog-react包时依然能正确取值。useEntity的取值逻辑useEntity.tsx相当严格若在没有任何 Provider 的环境中调用会抛出Entity context is not available若实体尚未加载完成会抛出useEntity hook is being called outside of an EntityLayout where the entity has not been loaded...并提示如果确实需要处理实体尚未加载的情况应改用useAsyncEntity。与之配套的还有useAsyncEntityuseEntity.tsx返回{ entity, loading, error, refresh }四元组适合需要展示加载态、错误态或手动刷新的场景EntityProvider/AsyncEntityProvideruseEntity.tsx负责向子树注入实体这也是测试插件时最常用的注入手段。3.2 测试时如何注入实体由于useEntity依赖 Context单元测试中需要使用 Provider 包裹被测组件。仓库中的 useEntity.test.tsx 与测试工具 createTestEntityPage.tsx 展示了标准的注入与断言方式——用EntityProvider或AsyncEntityProvider包裹组件并传入构造好的Entity对象即可。这也是插件作者为实体内容组件编写测试时的推荐做法。四、把插件嵌入实体页EntityPage.tsx 的三处改动4.1 找到实体页文件实体页位于 Backstage App 根目录的packages/app/src/components/Catalog/EntityPage.tsx说明当前仓库的示例 App 位于 packages/app-legacy/src/components/catalog/EntityPage.tsx而脚手架模板中的对应文件位于 packages/create-app/templates/legacy-app/packages/app/src/components/catalog/EntityPage.tsx。你自建 App 中的实际路径为packages/app/src/components/Catalog/EntityPage.tsx。4.2 第一步导入插件组件在EntityPage.tsx顶部导入你的插件导出内容import { MyPluginEntityContent } from backstage/plugin-my-plugin;4.3 第二步用 EntitySwitch 按实体 kind 分流不同 kind 的实体Component、API、Group、User、System、Domain……在目录中拥有各自独立的渲染页面。这种分流能力由EntitySwitch组件提供其典型结构如下export const entityPage ( EntitySwitch EntitySwitch.Case if{isKind(component)} children{componentPage} / EntitySwitch.Case if{isKind(api)} children{apiPage} / EntitySwitch.Case if{isKind(group)} children{groupPage} / EntitySwitch.Case if{isKind(user)} children{userPage} / EntitySwitch.Case if{isKind(system)} children{systemPage} / EntitySwitch.Case if{isKind(domain)} children{domainPage} / EntitySwitch.Case{defaultEntityPage}/EntitySwitch.Case /EntitySwitch );EntitySwitch 的底层原理查看 EntitySwitch.tsx 源码可以理解它的工作方式EntitySwitch.Case只是一个标记组件渲染为空通过attachComponentData(EntitySwitchCaseComponent, core.backstage.entitySwitch, true)打上标记EntitySwitch本身通过useAsyncEntity()拿到当前实体并用useElementFilter过滤出所有被标记的 Case 子元素逐个执行其if条件EntitySwitch.tsxif条件可以是同步函数也可以是返回Promiseboolean的异步函数——存在异步条件时EntitySwitch会切换到内部的AsyncEntitySwitch分支并发求值EntitySwitch.tsx通过renderMultipleMatches属性可控制命中策略默认first渲染第一个命中的 Case传all可渲染所有命中项没有任何条件命中时渲染不带if的默认 CasegetDefaultChildren。内置条件谓词if中常用的条件谓词定义在 conditions.ts均为输入实体、返回布尔值的高阶函数谓词作用说明isKind(kinds)按实体kind匹配支持字符串或字符串数组如isKind(component)、isKind([system, domain])isComponentType(types)匹配 kind 为component且spec.type匹配常用于按组件类型细分渲染isResourceType(types)匹配 kind 为resource且spec.type匹配同上isApiType(types)匹配 kind 为api且spec.type匹配同上isEntityWith({ kind, type })通用匹配器上述谓词的底层实现见 conditions.tsisNamespace(namespaces)按metadata.namespace匹配适合按命名空间区分渲染以上匹配均为大小写不敏感内部通过toLowerCase()比较。若你要扩展 Software Catalog 的实体模型新增一种自定义 kind就需要在EntitySwitch中新增一个 Case若只是给已有实体类型添加内容则修改对应的页面即可。4.4 第三步用 EntityLayout.Route 添加 Tab确定了插件要挂在哪种实体页后修改对应页面。例如要把插件挂到systemPage就在其EntityLayout中新增一个EntityLayout.Route即新增一个页面 Tabconst systemPage ( EntityLayout EntityLayout.Route path/ titleOverview Grid container spacing{3} alignItemsstretch Grid item md{6} EntityAboutCard / /Grid Grid item md{6} EntityHasComponentsCard variantgridItem / /Grid Grid item md{6} EntityHasApisCard variantgridItem / /Grid Grid item md{6} EntityHasResourcesCard variantgridItem / /Grid /Grid /EntityLayout.Route EntityLayout.Route path/diagram titleDiagram EntityCatalogGraphCard variantgridItem height{400} / /EntityLayout.Route {/* Adding a new tab to the system view */} EntityLayout.Route path/your-custom-route titleCustomTitle MyPluginEntityContent / /EntityLayout.Route /EntityLayout );关键点path是该 Tab 在实体页内的路由片段title是 Tab 显示名称Route 内放入你的插件组件该组件即可通过useEntity拿到当前实体EntityLayout内部见 EntityLayout.tsx会建立子路由并注入实体 Context——这正是第三节中useEntity能取到实体的来源。仓库示例 EntityPage.tsx 中还展示了通过parentEntityRelations与UNSTABLE_contextMenuOptions定制实体页行为的用法。保存后打开任意一个 System 实体的详情页就能在导航 Tab 中看到名为CustomTitle的新 Tab点击即进入MyPluginEntityContent渲染的内容。五、新前端系统下的对应方案迁移指引本文主体基于旧版前端系统。在新版前端系统中实体页集成不再需要手工修改EntityPage.tsx而是通过蓝图Blueprint声明式注册EntityContentBlueprint用于创建展示在实体页上的内容块相当于EntityLayout.Route的内容导出自backstage/plugin-catalog-react/alphaEntityCardBlueprint用于创建展示在实体页上的卡片相当于EntityAboutCard这类卡片。相关用法详见 Common Extension Blueprints。特别地官方建议避免使用convertLegacyEntityCardExtension/convertLegacyEntityContentExtension将旧版扩展转换到新系统因为直接用蓝图注册效果更好旧转换器仅用于适配你无法控制的第三方插件。对于已有旧版实体页插件的团队可参考 插件结构文档 与 新后端系统文档 规划渐进式迁移。六、实战小结与注意事项把插件集成进 Software Catalog 的完整链路为yarn new创建frontend-plugin类型插件插件内用useEntity或useAsyncEntity读取当前实体必要时用EntityProvider包裹以便测试在 App 的 EntityPage.tsx 中导入插件组件用EntitySwitchisKind/isComponentType等谓词决定插件内容出现的实体种类用EntityLayout.Route为插件内容新增 Tab 页面。实践中的几个要点useEntity在实体未加载或脱离实体页 Context 时会直接抛错需要容忍加载态的组件请使用useAsyncEntityEntitySwitch的if条件支持异步谓词可通过renderMultipleMatchesall让多个 Case 同时生效如果你在扩展 Catalog 的实体模型自定义 kind记得在EntitySwitch顶层新增对应 Case而不是只改单个页面该集成方式为实验性能力升级 Backstage 版本时留意相关 API 变更新版前端系统请优先采用EntityContentBlueprint/EntityCardBlueprint。通过以上步骤你可以把诸如 CI 状态、部署信息、安全扫描结果等任意自定义内容以 Tab 的形式无缝嵌入到实体详情页中让 Software Catalog 真正成为围绕实体组织开发信息的统一入口。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考