ARTICLE DETAIL

资讯详情

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

Backstage example-app 详解:基于新前端系统构建开发者门户前端应用

Backstage example-app 详解:基于新前端系统构建开发者门户前端应用 Backstage example-app 详解基于新前端系统构建开发者门户前端应用【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage在 Backstage 仓库中packages/app 是一个名为example-app的示例前端包它使用官方推荐的新前端系统New Frontend System组装出完整的开发者门户界面软件目录Catalog、脚手架Scaffolder、搜索Search、TechDocs、Kubernetes、通知与用户设置等。本文将以 packages/app/README.md 为核心骨架结合 App.tsx、package.json 与各功能模块源码完整讲解这个应用包的角色、依赖构成、启动方式以及在新前端系统下的装配原理。读完本文你将能够在本地启动该示例应用并理解它如何通过createApp、Feature 模块与扩展蓝图Blueprint组合出整个门户。一、包的定位main example app 与backstage.roleREADME 对包定位只有一句话但信息量很足This package is the main example Backstage application using the new frontend system.也就是说packages/app既是仓库自带的可运行示例也是官方展示新前端系统写法的“参考实现”。这一点可以从 docs/frontend-system/index.md 得到印证新前端系统文档在 “Status” 一节明确写道插件迁移到新前端系统时应使用/alpha子路径导出并指向apppackage 作为示例应用配置。在 packages/app/package.json 中可以看到包的关键元信息{ name: example-app, backstage: { role: frontend }, private: true }name为example-app与目录名app不同它是 monorepo 内部消费的私有包private: truebackstage: { role: frontend }是 Backstage 约定字段用于向 CLI 和工具链声明该包的类型前端应用区别于后端包role: backend与纯库package.json#L108 中还有bundled: true表明其构建产物会以打包方式分发。二、本地启动两个终端前后端都要跑README 给出的启动方法非常简单直接继承如下yarn start同时 README 附了一条重要的注意事项NOTE:Dont forget to open a second terminal and to launch the backend there, usingyarn start! The frontend requires a backend to connect to.即前端本身不能独立工作它需要连接一个正在运行的 Backstage 后端提供 Catalog、Auth、Search 等 API因此需要打开第二个终端再启动一次后端。这也是 Backstage 仓库本地开发的固定模式。结合 packages/app/package.json#L18-L24包内所有脚本都委托给 Backstage CLI 完成这也是 Backstage 工作区的统一约定scripts: { build: backstage-cli package build, clean: backstage-cli package clean, lint: backstage-cli package lint, start: backstage-cli package start, test: backstage-cli package test }yarn start经由backstage-cli package start启动带热更新的开发服务器yarn test运行本包的单元/组件测试应用级配置则来自仓库根目录的 app-config.yaml含认证、Catalog 位置、集成等全局配置由backstage/config加载。三、依赖构成一个门户需要哪些官方插件从 packages/app/package.json#L37-L94 的dependencies可以看出这个示例应用装配了几乎全部核心官方插件可以按职责分为四组分组代表依赖作用前端系统基础设施backstage/frontend-defaults、backstage/frontend-app-api、backstage/frontend-plugin-api、backstage/core-app-api、backstage/core-compat-api、backstage/app-defaults提供createApp、扩展蓝图、路由/权限/配置等核心 API以及新旧系统兼容层核心功能插件backstage/plugin-catalog、backstage/plugin-scaffolder、backstage/plugin-search、backstage/plugin-techdocs、backstage/plugin-org、backstage/plugin-home、backstage/plugin-notifications、backstage/plugin-user-settings、backstage/plugin-signals目录、模板、搜索、文档、组织架构、首页、通知、信号等门户功能辅助/工具类backstage/plugin-devtools、backstage/plugin-app-visualizer、backstage/plugin-catalog-import、backstage/plugin-catalog-unprocessed-entities开发调试、插件树可视化、批量导入、未处理实体视图UI 与运行时react^18、react-router(-dom)^6.30.2、material-ui/*4.x、backstage/theme、backstage/ui、history^5React 18 React Router 6 Material UI 4 的渲染底座Backstage 4.x 时代仍使用 MUI4 体系devDependencies中另含playwright/test、testing-library/*等对应包内的单元与 E2E 测试见第七节。四、入口链路从index.tsx到createApp启动链路很短先看 packages/app/src/index.tsximport backstage/cli/asset-types; import ReactDOM from react-dom/client; import app from ./App; import backstage/ui/css/styles.css; ReactDOM.createRoot(document.getElementById(root)!).render(app);它用 React 18 的createRoot把默认导出的app一个可渲染的 root 元素挂到#root节点上。真正的装配逻辑在 packages/app/src/App.tsxconst app createApp({ features: [ customizedCatalog, pagesPlugin, convertedTechdocsPlugin, userSettingsPlugin, homePlugin, appVisualizerPlugin, kubernetesPlugin, notFoundErrorPageModule, appModuleNav, appModuleHome, appModuleScaffolder, ...collectedLegacyPlugins, ], advanced: { pluginInfoResolver, }, }); export default app.createRoot();这里的要点createApp来自backstage/frontend-defaults——新前端系统的“默认装配器”传入features数组各类 Feature返回的对象再经app.createRoot()生成最终 React 树。createFrontendModule、PageBlueprint等构建原语则来自backstage/frontend-plugin-apifeatures就是应用的能力清单每一项可以是一个插件plugin-catalog/alpha、一个由createFrontendModule包起来的扩展集合或经旧版转换得到的 Featureadvanced.pluginInfoResolver指向本包的 pluginInfoResolver.ts用于在运行时解析各插件的版本/来源元信息供 App Visualizer 等工具消费。五、应用级功能模块src/modules/README 提到的“main example application”具体长什么样主要由src/modules/下的三个应用级模块决定。它们不隶属任何官方插件而是应用自己用createFrontendModule注册的“App 私有 Feature”。5.1 侧边栏导航appModuleNavappModuleNav.tsx 通过NavContentBlueprint.make提供了整个门户的左侧导航组件export const appModuleNav createFrontendModule({ pluginId: app, extensions: [ NavContentBlueprint.make({ params: { component: ({ navItems }) { const nav navItems.withComponent(item ( SidebarItem icon{() item.icon} to{item.href} text{item.title} / )); // Consume without rendering — handled by the search modal nav.take(page:search); return ( Sidebar SidebarLogo / SidebarGroup labelSearch icon{SearchIcon /} to/search SidebarSearchModal / /SidebarGroup SidebarGroup labelMenu icon{MenuIcon /} {nav.take(page:home)} {nav.take(page:catalog)} {nav.take(page:scaffolder)} SidebarScrollWrapper{nav.rest({ sortBy: title })}/SidebarScrollWrapper /SidebarGroup SidebarGroup labelSettings icon{UserSettingsSignInAvatar /} to/settings NotificationsSidebarItem / {nav.take(page:devtools)} {nav.take(page:user-settings)} /SidebarGroup /Sidebar ); }, }, }), ], });从源码结构看这段代码展示了新前端系统的一个关键协作模式导航条目由各个插件声明page:home、page:catalog、page:devtools 等由应用层的 Nav 扩展负责“点名取用”nav.take(...)并按顺序排版。注意nav.take(page:search)一行——搜索项被取出但不渲染因为它改由SidebarSearchModal来自backstage/plugin-search以全局模态框形式呈现。侧边栏底部还集成了NotificationsSidebarItem通知与UserSettingsSignInAvatar头像/签入说明应用模块可以自然地引用各官方插件导出的组件。5.2 首页自定义卡片appModuleHomeappModuleHome.tsx 演示了如何给 Home 页追加一张自定义 Widgetconst gettingStartedWidget HomePageWidgetBlueprint.make({ name: getting-started, params: { name: GettingStarted, title: Getting Started, description: Tips and links to help you get started with Backstage, components: async () ({ Content: () MarkdownContent content{content} /, }), }, }); export const appModuleHome createFrontendModule({ pluginId: home, extensions: [gettingStartedWidget], });注意pluginId: home——应用模块也可以“挂”到某个插件名下向它的HomePageWidgetBlueprint蓝图注入扩展实现“不改插件、只改应用”的定制方式。其 Markdown 内容里还内置了使用提示想删掉这张卡片删除该文件并移除 App.tsx 中的appModuleHome导入与注册即可——这正是新前端系统“功能即 Feature、移除即消失”的可组合性的体现。5.3 其他模块同目录下的 appModuleScaffolder.tsx 以同样方式为 Scaffolder 页提供应用级定制模板卡片样式等src/modules/目录整体就是“应用私有 Feature”的存放约定。六、示例特性src/examples/自定义插件的完整教学样例src/examples/下两个文件是 README 所述“main example application”最有教学价值的部分它们是一个完整的自定义前端插件兼作新前端系统 API 的活文档。6.1pagesPlugin页面、路由、Feature Flag 与权限谓词pagesPlugin.tsx 用createFrontendPlugin定义了一个pluginId为pages的插件export const pagesPlugin createFrontendPlugin({ pluginId: pages, info: { packageJson: () import(../../package.json), manifest: () import(../../catalog-info.yaml), }, routes: { page1: page1RouteRef, pageX: pageXRouteRef }, externalRoutes: { pageX: externalPageXRouteRef }, featureFlags: [ { name: experimental-features }, { name: advanced-features }, { name: beta-access }, { name: experimental-card }, ], extensions: [ IndexPage, Page1, ExternalPage, FeatureFlagPage, AllFlagsPage, AnyFlagPage, PermissionCardPage, PublicCard, RestrictedCard, PermissionGatedPage, PermissionActionPage, FeatureFlagCard ], });这个示例覆盖了新前端系统的多组核心 APIPageBlueprint.make以声明式方式定义页面params中包含path路由路径、routeRef供其他插件引用的路由句柄与loader懒加载组件工厂。如 IndexPage 注册在/路径并示范了useRouteRef、createExternalRouteRef外部路由供 iframe 嵌入场景使用等用法条件启用谓词if这是新前端系统的特色能力扩展页面或卡片在应用启动时按谓词求值不满足则根本不会进入路由树。示例给出了四种典型写法单一 Feature Flagif: { featureFlags: { $contains: experimental-features } }FeatureFlagPage全部满足$all组合两个 flag 条件AllFlagsPage任一满足$any组合两个 flag 条件AnyFlagPage权限门控if: { permissions: { $contains: catalog.entity.create } }PermissionGatedPage以及带#action后缀的细粒度写法{ permissions: { $contains: catalog.entity.read#read } }PermissionActionPage#前是权限名、后是动作作为attributes.action传给权限 API。输入注入Extension InputPermissionCardPage 用createExtensionInput声明了一个cards输入页面本身始终可见而内部的 PublicCard、RestrictedCard需catalog.entity.create权限与 FeatureFlagCard需experimental-cardflag各自携带独立的if谓词按需实例化——展示了“页面级”与“组件级”条件启用的组合。Feature Flag 注册插件通过顶层featureFlags数组声明四个 flag用户在设置页开关后刷新应用即可看到对应页面出现/消失验证流程与源码注释中的说明一致。6.2 自定义 404 页SwappableComponentBlueprintnotFoundErrorPageExtension.tsx 演示了用“可替换组件”覆盖系统默认行为export default SwappableComponentBlueprint.make({ name: not-found-error-page, params: define define({ component: NotFoundErrorPage, loader: () CustomNotFoundErrorPage, }), });它针对NotFoundErrorPage这一系统组件注册了一个自定义加载器一个居中的 404 页面含“Go home”按钮随后在 App.tsx#L75-L78 中被createFrontendModule({ pluginId: app, extensions: [notFoundErrorPage] })包成模块注册。这类“可替换组件”是主题化、品牌化门户时的标准改造点。七、旧版系统的兼容层convertLegacy*工具新前端系统与 1.x 时代的前端系统并存迁移App.tsx里恰好把三种兼容手段都示范了一遍convertLegacyPluginApp.tsx#L51-L62把旧版 TechDocs 插件连同其页面/实体内容扩展包装成新前端系统的 Feature。源码注释特别说明TechDocs 本身已支持新系统此处转换只是为了演示工具用法const convertedTechdocsPlugin convertLegacyPlugin(techdocsPlugin, { extensions: [ convertLegacyPageExtension(TechDocsIndexPage, { name: index, path: /docs }), convertLegacyPageExtension(TechDocsReaderPage, { path: /docs/:namespace/:kind/:name/* }), convertLegacyEntityContentExtension(EntityTechdocsContent), ], });convertLegacyAppRootFlatRoutesApp.tsx#L80-L84把旧式 JSX 路由树如CatalogImportPage的/catalog-import路由批量转换成新 Feature 列表collectedLegacyPluginswithOverridesApp.tsx#L64-L73对已完全适配新系统的 catalog 插件做“外科手术式”定制——这里把 Overview 页签的图标覆盖为InfoIcon展示如何用plugin.getExtension(entity-content:catalog/overview).override({ params: {...} })精确修改某个扩展的参数。这三个工具均来自backstage/core-compat-api是存量应用向新前端系统迁移时的核心桥接件示例包把它们放在同一个文件里构成了一个完整的迁移样板。八、质量保障单元测试与 E2E 测试该包自带完整的测试设施与 package.json 中的testing-library/*、playwright/test等 devDependencies 对应组件测试App.test.tsx 验证应用根部渲染行为E2E 测试e2e-tests/目录下有三个用例——app.test.ts应用整体冒烟、HomePage.test.ts首页内容断言、SearchPage.test.ts搜索页由仓库根目录的 playwright.config.ts 统一驱动backstage/e2e-test、backstage/e2e-test-utils提供测试辅助能力。九、小结packages/app给出的“新前端系统应用”参考范式把 README 的两句话展开后packages/app实际上回答了三个问题怎么跑仓库内两个终端各执行一次yarn start前端 后端脚本全部委托backstage-cli package *怎么组装以createApp({ features, advanced })为中心功能以 Feature插件或createFrontendModule模块为单位声明式注册应用私有定制集中在src/modules/怎么扩展与兼容新插件用createFrontendPlugin 各类 BlueprintPageBlueprint、SwappableComponentBlueprint、HomePageWidgetBlueprint、NavContentBlueprint构建支持if谓词按权限/Feature Flag 条件启用旧插件则通过convertLegacyPlugin、convertLegacyAppRoot、withOverrides渐进迁移。对需要在自有 Backstage 实例中新增页面、定制导航、门控功能或迁移存量插件的开发者这个包是仓库内最直接、最完整的可对照实现。【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表