Backstage 前端插件脚手架实战:从 `yarn new` 生成插件到独立运行与验证
2026/9/11 13:56:23 网站建设 项目流程

Backstage 前端插件脚手架实战:从yarn new生成插件到独立运行与验证

【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage

本篇技术指南以 Backstage 官方 golden-path 教程的前端插件第一步为主线,完整讲解如何通过 CLI 的yarn new --select frontend-plugin命令在当前仓库中脚手架出一个全新的前端插件包,逐文件剖析脚手架生成的代码结构,并给出在完整应用与独立开发服务器两种模式下运行、验证插件的完整步骤。读完本文,你将掌握 Backstage 新前端系统(Frontend System)下插件包的创建流程、createFrontendPlugin/PageBlueprint的插件定义方式,以及常见的脚手架故障排查方法。

在开始之前:环境与前置条件

脚手架插件的第一步是确保你的仓库处于可构建状态。根据官方 golden-path 文档与 CLI 模块文档,有两个关键前置条件:

  1. 安装依赖:必须在仓库根目录执行过yarn install,否则yarn new在安装阶段可能失败(这也是文档「常见问题」一节明确提到的场景)。
  2. Node.js 版本匹配:确保本机 Node.js 版本与项目要求的版本一致,类型检查与脚手架生成依赖 Node 运行时行为。

此外需要理解一个关键机制:命令中的yarn new并非独立的 CLI 命令,而是根目录package.json中定义的 script 别名,其背后指向backstage-cli new。这一点可以从 module-new 文档 得到印证——它给出了根package.json中典型的 script 配置:

{ "scripts": { "new": "backstage-cli new" } }

backstage-cli new默认会打开一个交互式引导界面,而通过--select--option参数则可以跳过交互、直接生成目标类型的包。

用一条命令脚手架出插件

在 Backstage 仓库根目录执行以下命令,即可创建一个全新的前端插件包:

yarn new --select frontend-plugin --option pluginId=todo --option owner=

命令各部分的含义如下:

参数作用
--select frontend-plugin预选要创建的目标类型为「前端插件」。不传该参数时,CLI 会进入完全交互式引导
--option pluginId=todo指定插件 ID 为todo,它决定了插件的目录名、路由路径与包名的关键部分
--option owner=指定插件的维护者/归属信息,此处传空字符串表示暂不设置

backstage-cli new的完整用法与更多参数可参考 CLI 新模块文档。

命令执行完成后,会在plugins/todo目录下生成一个新的 NPM 包(目录路径取决于你选择的插件 ID),包名形如@internal/plugin-todo——具体名称取决于传给new命令的参数以及仓库根目录package.json中的相关设置。

脚手架生成的目录结构

创建插件需要一点时间,命令结束后,你会得到如下结构的插件包:

plugins/todo/ ├── dev/ # Standalone dev server setup ├── src/ │ ├── components/ │ │ ├── TodoList/ │ │ └── TodoPage/ │ └── ... # Plugin definition, routes, tests └── package.json

这一结构的模板真实存在于本仓库的 packages/cli-module-new/templates/frontend-plugin/ 目录中,你可以对照模板.hbs文件理解每个生成文件背后的参数化逻辑(例如pluginIdpackageName等占位符如何被替换)。

深入生成的代码:你的第一个插件是如何构成的

官方 golden-path 系列第二篇「探索生成的代码」逐文件讲解了这些代码的含义,这里结合仓库中的模板源码做一次完整剖析。

src/plugin.tsx—— 插件定义与页面扩展

这是插件的核心定义文件。生成代码对应模板 plugin.tsx.hbs:

import { createFrontendPlugin, PageBlueprint, } from '@backstage/frontend-plugin-api'; import { rootRouteRef } from './routes'; export const page = PageBlueprint.make({ params: { path: '/todo', routeRef: rootRouteRef, loader: () => import('./components/TodoPage').then(m => ( <m.TodoPage /> )), }, }); export const todoPlugin = createFrontendPlugin({ pluginId: 'todo', extensions: [page], routes: { root: rootRouteRef, } });

关键点解读:

  • createFrontendPlugin将插件注册到 Backstage 前端系统中,pluginId: 'todo'是全局唯一的插件标识;
  • PageBlueprint.make定义了一个「页面扩展」——它声明了应用中的一个路由(path: '/todo'),并通过loader对页面组件做懒加载(按需加载,减少首屏体积);
  • rootRouteRef是一个路由引用(Route Reference),其他插件可以通过它生成指向你插件页面的链接,实现插件间的导航互通。其定义本身非常简单,见模板 routes.ts:
import { createRouteRef } from '@backstage/frontend-plugin-api'; export const rootRouteRef = createRouteRef();

注意生成代码中的pathpluginId都是模板变量:模板文件中的{{pluginId}}会在脚手架时被替换为你传入的插件 ID。也就是说,如果你把pluginId改为my-plugin,路由路径会相应变成/my-plugin

src/index.ts—— 包入口

包的入口文件默认导出插件实例(对应模板 index.ts.hbs):

export { todoPlugin as default } from './plugin';

默认导出(而非命名导出)是 Backstage 插件包的约定——这与仓库中 ADR003:避免默认导出 讨论的应用代码风格相反,插件包本身需要以默认导出形式暴露插件实例,以便 feature discovery 机制自动识别。

src/plugin.test.ts—— 插件定义冒烟测试

脚手架同时生成了插件定义的测试(对应模板 plugin.test.ts.hbs):

import { todoPlugin } from './plugin'; describe('todo', () => { it('should export plugin', () => { expect(todoPlugin).toBeDefined(); }); });

它验证插件实例能够被正确创建与导出,属于最基础的冒烟测试。

src/components/TodoPage/—— 页面组件与数据获取

TodoPage是插件的主页面组件,负责从后端获取数据并渲染(对应模板 TodoPage.tsx.hbs):

import { Progress } from '@backstage/core-components'; import { useApi, fetchApiRef, } from '@backstage/frontend-plugin-api'; import { Header, Container } from '@backstage/ui'; import useAsync from 'react-use/esm/useAsync'; import { TodoList } from '../TodoList'; import type { TodoItem } from '../TodoList'; const exampleTodos: TodoItem[] = [ { id: '1', title: 'Install the backend plugin', createdBy: 'user:default/guest', createdAt: new Date().toISOString() }, { id: '2', title: 'Connect the frontend to real data', createdBy: 'user:default/guest', createdAt: new Date().toISOString() }, ]; function useTodos() { const { fetch } = useApi(fetchApiRef); return useAsync(async (): Promise<TodoItem[]> => { const response = await fetch(`plugin://todo/todos`); if (!response.ok) { throw new Error( `Failed to fetch todos: ${response.status} ${response.statusText}`, ); } const data = await response.json(); return data.items; }, [fetch]); } export const TodoPage = () => { const { value: todos, loading, error } = useTodos(); if (loading) { return <Progress />; } return ( <> <Header title="Welcome to todo!" /> <Container> <TodoList todos={error ? exampleTodos : (todos ?? [])} /> </Container> </> ); };

这里蕴含了 Backstage 前端系统最重要的 API 使用模式:

  • fetchApiRef是 Backstage 提供的 fetch 封装 API(在@backstage/frontend-plugin-api中导出)。它包装了浏览器原生fetch,自动完成两件关键事情:
    1. 自动注入认证凭据——无需手动拼接任何Authorization头;
    2. 解析plugin://<pluginId>URL scheme——将plugin://todo/todos解析为当前实例后端插件的真实地址(例如http://localhost:7007/api/todo/todos),具体的解析依赖 discovery 机制,开发中无需关心后端地址的配置。
  • useAsync(来自react-use)在组件挂载时执行异步函数,返回{ value, loading, error },组件据此呈现三种状态:加载中转圈(Progress)、后端请求失败时回退到示例数据exampleTodos,保证插件开箱即可渲染)、成功时展示真实 todo 列表;
  • 页面通过@backstage/uiHeaderContainer维持与 Backstage 其他插件一致的视觉风格。

src/components/TodoList/—— 纯展示组件

TodoList是一个纯展示(presentational)组件,接收 todos 数组作为 props 并渲染成表格(见模板 TodoList.tsx):

import { Table, useTable, CellText, type ColumnConfig } from '@backstage/ui'; export type TodoItem = { title: string; id: string; createdBy: string; createdAt: string; }; const columns: ColumnConfig<TodoItem>[] = [ { id: 'title', label: 'Title', isRowHeader: true, cell: item => <CellText title={item.title} />, }, { id: 'createdBy', label: 'Created by', cell: item => <CellText title={item.createdBy} />, }, { id: 'createdAt', label: 'Created at', cell: item => <CellText title={new Date(item.createdAt).toLocaleString()} />, }, ]; export const TodoList = ({ todos }: { todos: TodoItem[] }) => { const { tableProps } = useTable({ mode: 'complete', data: todos, paginationOptions: { pageSize: todos.length || 1 }, }); return ( <Table columnConfig={columns} {...tableProps} pagination={{ type: 'none' }} /> ); };

TodoItem类型与后端插件返回的数据形状一一对应,是前后端约定的数据契约;TableColumnConfiguseTable均来自@backstage/ui。在较新版本的脚手架中,UI 组件已从@backstage/core-components逐步迁移到统一的@backstage/ui包(Progress仍来自@backstage/core-components)。

dev/index.tsx—— 独立开发服务器

插件还带有一套独立的开发环境(模板 dev/index.tsx):

import { createDevApp } from '@backstage/frontend-dev-utils'; import plugin from '../src'; createDevApp({ features: [plugin] });

createDevApp会构建一个仅加载当前插件的迷你 Backstage 应用,使你可以在不启动整个平台的情况下快速迭代插件 UI。

package.json——backstage.role决定构建行为

生成的package.json(对应模板 package.json.hbs)中有两个值得注意的字段:

{ "name": "@internal/plugin-todo", "main": "src/index.ts", "types": "src/index.ts", "backstage": { "role": "frontend-plugin", "pluginId": "todo" }, "scripts": { "start": "backstage-cli package start", "build": "backstage-cli package build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "clean": "backstage-cli package clean" } }
  • backstage.role: "frontend-plugin"告知 Backstage 工具链如何构建与对待该包,是决定包类型的关键字段;
  • 所有脚本都委托给backstage-cli package ...,因此yarn startyarn test等操作在插件目录内即可直接使用;
  • 依赖方面,插件默认依赖@backstage/frontend-plugin-api@backstage/core-components@backstage/ui@backstage/theme等,并将react声明为 peer dependency。

运行并验证你的插件

在完整应用中验证(feature discovery)

如果你的应用开启了 feature discovery(默认开启),插件会被自动发现并安装。开启方式是在app-config.yaml中设置(这也是仓库根 app-config.yaml 中的默认配置):

app: packages: all

packages: all表示自动发现应用包依赖中的全部插件;你也可以改用include/exclude过滤列表精确控制,例如:

app: packages: exclude: - '@internal/plugin-todo'

关于 feature discovery 的完整机制与手动安装方式,可参考安装插件文档(手动安装适用于未开启 discovery 或需要精确控制插件顺序的场景)。

确认配置无误后,从仓库根目录启动完整应用:

yarn start

然后在浏览器中访问http://localhost:3000/todo(路径与你选择的插件 ID 一致)。你会看到一个带标题栏和示例数据的 todo 页面;如果后端 todo 插件也在运行,页面将显示真实数据而非示例数据。

独立运行插件

如果只想专注于当前插件的开发,可在插件目录对应的 workspace 上启动独立开发服务器:

yarn workspace @internal/plugin-todo start

这条命令会启动dev/index.tsx中配置的独立开发应用,实现秒级热更新的插件级开发体验。

常见问题排查

官方文档针对脚手架过程中的高频问题给出了三条排查路径:

插件页面没有显示

检查app-config.yamlapp.packages是否设置为all。如果你使用了 include/exclude 过滤规则,请确认你的插件包没有被排除在外。

`yarn new` 在安装阶段失败

确保你已经在仓库根目录先执行过yarn install,并且本机 Node.js 版本与项目要求的版本一致。

脚手架后出现 TypeScript 类型错误

在仓库根目录运行yarn tsc检查类型错误。新脚手架出的插件应当能够无错误编译——如果报错,尝试重新执行yarn install

下一步:从脚手架走向完整的插件

本文覆盖了插件从创建到运行验证的完整闭环。脚手架只是一个起点,golden-path 教程的后续章节会带你深入插件开发的进阶主题,建议按顺序继续阅读:

  1. 探索生成的代码:进一步理解插件定义、页面组件与 UI 组件之间的协作方式;
  2. 动态配置:利用前端系统「配置优先(config-first)」的特性,通过app-config.yaml在不改代码的前提下禁用扩展、修改页面标题,甚至用PageBlueprint.makeWithOverrides+configSchema(基于 Standard Schema,仓库示例使用 Zod)添加自定义可配置项;
  3. HTTP 客户端:深入fetchApiRefplugin://URL scheme 的协作原理,将数据请求抽取为独立 Client 类,或借助 OpenAPI schema 生成类型安全的客户端,避免前后端漂移;
  4. 测试:用 Jest + React Testing Library + MSW 编写单元测试,用 Playwright 编写端到端集成测试。

至此,你已经掌握了 Backstage 前端插件从「一条命令生成」到「运行验证」再到「进阶扩展」的完整路径,可以在此基础上开始构建自己的开发者门户插件了。

【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询