十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Backstage 插件结构详解:从目录骨架到扩展接入的完整实战指南

Backstage 插件结构详解:从目录骨架到扩展接入的完整实战指南 Backstage 插件结构详解从目录骨架到扩展接入的完整实战指南【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage本篇技术指南聚焦 Backstage 前端插件的标准目录结构与组成要素以官方structure-of-a-plugin文档为骨架结合仓库内真实插件plugins/example-todo-list、CLI 模板packages/cli-module-new/templates/legacy-frontend-plugin以及组合系统文档docs/plugins/composability.md进行源码级印证。读完本篇你将掌握插件目录中每个文件的作用、createPlugin与createRoutableExtension的用法、插件扩展在应用中的接入方式以及插件与外部服务通信的代理方案。:::note[文档适用范围说明] 本文描述的是旧前端系统legacy frontend system下的插件结构。Backstage 已推出新的前端系统相关说明见 Building Frontend Plugins。新旧系统的总体目录结构相似但plugin.ts中的插件接线方式差异显著——旧系统使用createPlugincreateRoutableExtension新系统则基于扩展蓝图Extension Blueprints。理解旧系统结构仍然是阅读大量存量插件源码的基础。 :::一、插件目录结构一个自包含的迷你项目使用 Backstage CLI如yarn new --select plugin或backstage-cli new --select plugin创建新插件后得到的目录大致如下new-plugin/ dev/ index.ts node_modules/ src/ components/ ExampleComponent/ ExampleComponent.test.tsx ExampleComponent.tsx index.ts ExampleFetchComponent/ ExampleFetchComponent.test.tsx ExampleFetchComponent.tsx index.ts index.ts plugin.test.ts plugin.ts routes.ts setupTests.ts .eslintrc.js package.json README.md这个结构有两个值得注意的设计意图插件本身就是独立的 npm 包。它自带package.json和src目录看起来像一个迷你项目。这样做的目的有两个一是插件可以独立发布到 npm二是可以在隔离环境中单独开发某个插件而无需在大型 Backstage 应用中加载所有其他插件。每个目录下的index.ts充当导出聚合层。通过index.ts其他代码可以从文件夹路径导入import { ExampleComponent } from ./components/ExampleComponent而不是逐个文件精确导入。这样可以将每个文件夹的对外导出集中控制在单个文件中。仓库中的真实案例完全印证了这一结构官方示例插件 plugins/example-todo-list 的src/下包含components/TodoList/、components/TodoListPage/、index.ts、plugin.test.ts、plugin.ts、routes.ts、setupTests.ts与文档描述一一对应CLI 的插件生成模板 packages/cli-module-new/templates/legacy-frontend-plugin 中保留了dev/index.tsx.hbs、src/components/ExampleComponent/、src/components/ExampleFetchComponent/、src/routes.ts.hbs、src/plugin.ts.hbs等全部文件说明上文目录正是脚手架实际产出的形态。二、基础文件README 与 package.json生成插件时你会拿到一个待填充的README.md和一个package.jsonREADME.md用于记录插件的信息、功能说明、配置方法等发布到 npm 或接入目录plugin directory时是重要的元信息载体package.json声明插件的依赖、元数据和脚本。以 plugins/example-todo-list/package.json 为例它包含backstage字段标记role: frontend-plugin、pluginId以及关联的pluginPackages前端、后端、公共包三件套scriptsbuild、clean、lint、start、test等全部通过backstage-cli package ...系列命令驱动dependencies通常包含backstage/core-plugin-api核心插件 API、backstage/core-componentsUI 组件以及渲染层所需的 React 与 Material UI 相关库peerDependencies声明react、react-dom、react-router-dom等宿主运行时依赖types/react被标记为 optional。三、插件文件 plugin.ts插件的创建与扩展导出src目录下的核心文件是plugin.ts示例内容如下import { createPlugin, createRoutableExtension, } from backstage/core-plugin-api; import { rootRouteRef } from ./routes; export const examplePlugin createPlugin({ id: example, routes: { root: rootRouteRef, }, }); export const ExamplePage examplePlugin.provide( createRoutableExtension({ name: ExamplePage, component: () import(./components/ExampleComponent).then(m m.ExampleComponent), mountPoint: rootRouteRef, }), );这段代码做了两件事createPlugin创建插件实例传入id全局唯一标识和routes插件对外暴露的路由引用。CLI 模板中的实现与之完全一致见 plugin.ts.hbs只是插件名、扩展名、id由脚手架参数动态填充。plugin.provide()创建并导出扩展这里导出的是一个routable extension可路由扩展。createRoutableExtension要求提供name扩展名称component一个懒加载函数返回import(...).then(m m.xxx)mountPoint该组件对应的RouteRef是外部世界访问此组件的句柄其他组件或插件通过它来链接到这个可路由组件。可路由扩展强制使用懒加载这也是保证插件按需加载、隔离插件内部崩溃的关键。在 plugins/example-todo-list/src/plugin.ts 中可以看到真实写法todoListPlugin通过routes: { root: rootRouteRef }注册路由TodoListPage通过todoListPlugin.provide(createRoutableExtension({...}))导出component懒加载./components/TodoListPage。除了createRoutableExtension核心 API 还提供了createComponentExtension用于导出没有路由要求的普通 React 组件例如实体概览页上的卡片。组件扩展同样支持开箱即用的懒加载export const EntityFooCard plugin.provide( createComponentExtension({ component: { lazy: () import(./components/FooCard).then(m m.FooCard), }, }), );组件扩展和可路由扩展在导出时都会被包装以提供错误边界error boundary、懒加载和插件上下文。推荐将扩展的创建集中放在顶层plugin.ts或专门的extensions.ts或.tsx文件中而把具体实现放在懒加载的组件目录里保持plugin.ts轻量。关于createPlugin的完整 API 细节以及新组合系统的介绍可参见仓库中的 组合系统文档。3.1 routes.ts路由引用的独立文件plugin.ts中引用的rootRouteRef来自同目录下的routes.tsimport { createRouteRef } from backstage/core-plugin-api; export const rootRouteRef createRouteRef({ id: example, });将路由引用单独放在routes.ts中是一个重要约定避免在插件其他部分使用路由引用时产生循环导入circular imports。仓库示例 plugins/example-todo-list/src/routes.ts 中rootRouteRef的id为todo-list。3.2 扩展的命名模式为了让插件导出的符号意图清晰、避免导入别名组合系统约定了一套命名模式描述模式示例顶层页面*PageCatalogIndexPage、SettingsPage、LighthousePage实体页签内容Entity*ContentEntityJenkinsContent、EntityKubernetesContent实体概览卡片Entity*CardEntitySentryCard、EntityPagerDutyCard实体条件判断is*AvailableisPagerDutyAvailable、isJenkinsAvailable插件实例*PluginjenkinsPlugin、catalogPlugin工具 API 引用*ApiRefconfigApiRef、catalogApiRef存量插件向新组合系统迁移时旧的Router、*Card、plugin等导出名需要按此表重命名详见 组合系统文档中的迁移章节。四、组件目录页面组件与数据获取组件生成器会附带两个示例组件用于演示插件的组件组织方式ExampleComponent一个示例 Backstage 页面组件演示如何用Page、Header、Content等核心组件搭起页面骨架ExampleFetchComponent演示最常见的任务——向公共 API 发起异步请求并用 Material UI 的Table组件把响应数据渲染成表格见 ExampleFetchComponent.tsx.hbs。仓库中的真实页面 TodoListPage.tsx 展示了完整的页面组织模式外层使用Page带themeId、Header含HeaderLabel、Content、ContentHeader、SupportButton等backstage/core-components组件通过useApi获取discoveryApiRef、fetchApiRef、alertApiRef等工具 API用discoveryApi.getBaseUrl(todolist)得到后端基础地址后发起 fetch 请求数据展示组件 TodoList.tsx 使用react-use的useAsync管理加载状态加载中显示Progress出错显示Alert成功则渲染Table。一个插件通常有一个或多个页面组件旁边可以根据需要把 UI 拆分任意多个组件。这些示例组件可以随意修改、重命名或整体替换。五、把插件接入 Backstage 应用要让 Backstage 应用真正使用一个插件需要两步在app/package.json中把插件声明为依赖例如internal/plugin-todo-list: workspace:^在app/src/App.tsx中导入并使用一个或多个插件扩展例如将ExamplePage挂载到路由import { ExamplePage } from new-plugin; const routes ( FlatRoutes ... Route path/example element{ExamplePage /} / ... /FlatRoutes );好消息是使用 Backstage CLI 创建插件时这两个步骤都会自动完成。此外插件扩展必须位于从根AppProvider延伸出的同一棵 React 元素树中不要在应用里插入中间组件否则扩展无法正确解析。5.1 路由如何被解析组合系统的路由机制是每个RouteRef在运行时被绑定到一个具体的path而这个path是根据应用中的元素树发现的。例如Route path/foo element{FooPage /} /中的/foo会被关联到FooPage的挂载点fooPageRouteRef。之后其他组件可以通过useRouteRef钩子生成具体链接const MyComponent () { const fooRoute useRouteRef(fooPageRouteRef); return a href{fooRoute()}Link to Foo/a; };为避免插件之间产生不必要的直接依赖插件间跳转应使用ExternalRouteRef并在应用侧通过bindRoutes绑定详见 组合系统文档。从 Backstage 1.28 起ExternalRouteRef还支持defaultTarget默认目标和optional可选绑定。RouteRef还支持带命名参数的参数化路由如params: [name]以及相对固定路径的SubRouteRef。六、与外部世界通信代理Proxy方案如果你的插件需要与 Backstage 环境之外的服务通信通常会遇到两类问题CORS 策略浏览器直接跨域请求第三方服务会被同源策略拦截后端侧授权外部服务可能要求携带凭证或鉴权信息。为了平滑处理这些问题可以使用代理复用已有代理如 Nginx、HAProxy 等基础设施代理使用 Backstage 提供的 proxy-backend 插件这是官方为 Backstage 后端提供的代理方案插件通过/api/proxy路径转发请求从而绕开浏览器的跨域限制并在后端统一注入鉴权。详细配置与用法参见仓库内的 proxy-backend 插件说明 以及 代理文档。七、组合系统核心概念速览理解插件结构后再补充几个组合系统的核心概念完整说明见 组合系统文档它们直接决定了插件扩展的设计方式组件数据Component Data通过attachComponentData给 React 组件附加带 key 的数据渲染前可用getComponentData从 JSX 元素读取是路由与插件发现机制的底层支撑扩展Extensions插件为应用导出的产物绝大多数是 React 组件通过create*Extension创建并用plugin.provide()包装类型本质是{ expose(plugin: BackstagePlugin): T }路由引用RouteRef路由目标的间接句柄避免不同开源插件互相硬编码路径实体条件渲染catalog 插件提供EntitySwitch/EntitySwitch.Case配合isKind、isComponentType、isResourceType、isEntityWith、isNamespace等内置条件函数可为不同实体类型渲染不同内容。八、小结Backstage 插件本质是一个自包含的 npm 包package.json声明元数据与依赖src/plugin.ts通过createPlugin创建插件实例并provide出懒加载的扩展src/routes.ts独立维护路由引用以避免循环依赖src/components/存放页面与可复用组件src/index.ts收敛对外导出。接入应用只需两步——声明依赖并在App.tsx中挂载扩展与外部服务通信则通过 proxy-backend 代理解决跨域与鉴权问题。想动手实践可以直接参考仓库中的 example-todo-list 示例插件或使用backstage-cli new生成一个全新的插件脚手架逐步替换其中的示例组件即可完成第一个可运行页面的开发。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表