3. 插件开发基础:Backstage 插件架构、前端与后端分离、插件目录结构、Hello World 插件
3.1 Backstage 插件架构总览
Backstage 的插件系统采用前端与后端分离的微内核架构。每个插件都是一个独立的功能单元,通过统一的 App 壳(App Shell)进行集成。插件可以只包含前端(UI),也可以同时包含后端(Backend)以提供 API 或数据处理能力。
createPlugin 注册,后端通过 createBackendPlugin 注册。
3.2 前端与后端分离机制
Backstage 强制要求插件的前端和后端代码分离到不同的包中,这有助于职责清晰、独立部署和按需加载。
| 维度 | 前端(Frontend) | 后端(Backend) |
|---|---|---|
| 包名约定 | @internal/plugin-xxx |
@internal/plugin-xxx-backend |
| 入口文件 | src/index.ts + src/plugin.ts |
src/index.ts + src/plugin.ts |
| 主要依赖 | @backstage/core-plugin-api |
@backstage/backend-plugin-api |
| 注册方式 | createPlugin({...}) |
createBackendPlugin({...}) |
| 通信方式 | 通过 fetchApi 或 discoveryApi 调用后端 |
通过 httpRouter 暴露 RESTful API |
3.3 插件目录结构详解
一个标准的 Backstage 插件(以全栈插件为例)遵循以下目录结构。我们以 plugins/capacity-watermark 作为示例。
plugins/capacity-watermark/ # 插件根目录
├── package.json # 插件元数据与依赖
├── README.md # 插件说明文档
├── src/ # 前端源码
│ ├── index.ts # 前端入口,导出插件实例
│ ├── plugin.ts # 使用 createPlugin 注册
│ ├── components/ # React 组件
│ │ ├── CapacityCard.tsx
│ │ └── CapacityTable.tsx
│ ├── routes.ts # 路由定义
│ └── types.ts # TypeScript 类型定义
├── dev/ # 本地开发模拟数据
│ └── index.ts
├── config.d.ts # 插件配置类型声明
└── dist/ # 构建产物(自动生成)
如果插件包含后端,则在后端包中遵循类似结构:
plugins/capacity-watermark-backend/ # 后端插件根目录
├── package.json
├── src/
│ ├── index.ts # 后端入口
│ ├── plugin.ts # 使用 createBackendPlugin 注册
│ ├── service/ # 业务逻辑层
│ │ ├── CapacityService.ts
│ │ └── types.ts
│ ├── router.ts # 路由与 API 定义
│ └── run.ts # 独立运行入口(可选)
└── dist/
src/ 目录下。这会导致构建时打包进前端 bundle,暴露后端逻辑且无法独立部署。务必使用 -backend 后缀的独立包。
3.4 Hello World 插件实战
下面我们通过一个极简的 Hello World 插件,演示从零到注册完成的全流程。该插件只包含前端,在 Backstage 页面中显示“Hello, Backstage!”。
步骤 1:使用模板创建插件
在 Backstage 根目录执行以下命令,选择 plugin 模板:
yarn new --select plugin
# 输入插件名称:hello-world
# 选择前端插件(Frontend plugin)
命令执行后,自动生成 plugins/hello-world/ 目录及基础文件。
步骤 2:编写插件核心代码
编辑 plugins/hello-world/src/plugin.ts:
import { createPlugin, createRoutableExtension } from '@backstage/core-plugin-api';
import { rootRouteRef } from './routes';
export const helloWorldPlugin = createPlugin({
id: 'hello-world',
routes: {
root: rootRouteRef,
},
});
export const HelloWorldPage = helloWorldPlugin.provide(
createRoutableExtension({
name: 'HelloWorldPage',
component: () => import('./components/HelloWorldPage').then(m => m.HelloWorldPage),
mountPoint: rootRouteRef,
}),
);
编辑 plugins/hello-world/src/components/HelloWorldPage.tsx:
import React from 'react';
import { Content, Header, Page } from '@backstage/core-components';
export const HelloWorldPage = () => {
return (
<Page themeId="tool">
<Header title="Hello, Backstage!" />
<Content>
<p>这是您的第一个 Backstage 插件。</p>
</Content>
</Page>
);
};
步骤 3:注册到 App
在 packages/app/src/App.tsx 中添加路由:
import { HelloWorldPage } from '@internal/plugin-hello-world';
// 在 FlatRoutes 中添加
<Route path="/hello-world" element={<HelloWorldPage />} />
在 packages/app/src/components/Root/Root.tsx 中添加侧边栏菜单项:
import { SidebarItem } from '@backstage/core-components';
// 在 Sidebar 中添加
<SidebarItem icon={ExtensionIcon} to="/hello-world" text="Hello World" />
步骤 4:启动验证
在 Backstage 根目录执行 yarn dev,打开浏览器访问 http://localhost:3000/hello-world,应看到页面显示“Hello, Backstage!”标题。
- 侧边栏出现“Hello World”菜单项
- 点击菜单项后页面路由正确跳转
- 页面 Header 显示“Hello, Backstage!”
- 浏览器控制台无 404 或模块加载错误
3.5 本章小结
本章我们掌握了 Backstage 插件的前后端分离架构、标准目录结构,并通过 Hello World 插件走通了完整的开发与注册流程。这是后续实现容量水位巡检插件的基础骨架。