Skip to main content

构建前端插件

**注意:新的前台系统处于alpha阶段,只有少数插件支持。

本节介绍如何创建自己的前端插件推翻它们有时统称为前端。特点以及建立Backstage前台所需的安装程序应用.

创建新插件

本指南假定您已经建立了 Backstage 项目。 即使您只想开发一个用于发布的插件,我们仍建议您在标准 Backstage monorepo 项目中进行开发,因为您最终往往需要多个软件包。 有关如何建立新项目的说明,请参阅我们的入门文件。

要创建前端插件,请运行yarn new选择plugin这将在plugins/<pluginId>,这将是您插件的主要入口。

当前创建的插件将被模板化,以便在传统前端系统中使用,您需要替换现有的插件布线代码:

插件实例

前端插件的起点是createPlugin函数,它只接受一个选项对象作为参数。 它是从@backstage/frontend-plugin-api,在这里您可以找到大部分用于构建插件的常用 API。

这就是创建最小插件的方法:

in src/plugin.ts
import { createPlugin } from '@backstage/frontend-plugin-api';

export const examplePlugin = createPlugin({
id: 'example',
extensions: [],
});
in src/index.ts
export { examplePlugin as default } from './plugin';

请注意,我们将插件导出为软件包的默认导出方式,即从src/index.ts这一点很重要,因为这样我们插件的用户才能在Backstage应用程序中无缝安装插件包,而无需通过代码引用插件实例。

插件 ID 应该是一个以小写破折号分隔的字符串,而插件实例变量应该是 ID 的驼峰字母大小写版本,并带有一个Plugin有关前台系统命名模式的更多详情,请参阅关于命名模式的文章坚持使用这些命名模式,可以确保插件用户更容易识别插件提供的输出和功能。

添加扩展名

上面创建的插件是空的,不提供任何实际功能。 要为插件添加功能,需要创建一个或多个扩展让我们继续为插件添加一个独立页面,以及一个允许用户导航到该页面的导航项。

要创建新的扩展名,通常需要使用预定义的扩展创建者在这种情况下,我们需要使用createPageExtensioncreateNavItemExtension都来自@backstage/frontend-plugin-api我们还需要创建路线参考作为输出页面的参考,使我们能够动态创建链接到我们页面的 URL。

in src/routes.ts
import { createRouteRef } from '@backstage/frontend-plugin-api';

// Typically all routes are defined in src/routes.ts, in order to avoid circular imports.

// This will be the route reference for our example page. If you want to link
// to the page from somewhere else, you can use this reference to generate the target path.
export const rootRouteRef = createRouteRef();
in src/plugin.ts
import {
createPlugin,
createPageExtension,
createNavItemExtension,
} from '@backstage/frontend-plugin-api';
import { rootRouteRef } from './routes';

// Note that these extensions aren't exported, only the plugin itself is.
// You can export it locally for testing purposes, but don't export it from the plugin package.
const examplePage = createPageExtension({
routeRef: rootRouteRef,

// This is the default path of this page, but integrators are free to override it
defaultPath: '/example',

// Page extensions are always dynamically loaded using React.lazy().
// All of the functionality of this page is implemented in the
// ExamplePage component, which is a regular React component.
loader: () => import('./components/ExamplePage').then(m => <m.ExamplePage />),
});

// This nav item is provided to the app.nav extension, and will by default be rendered as a sidebar item
const exampleNavItem = createNavItemExtension({
routeRef: rootRouteRef,
title: 'Example',
icon: ExampleIcon, // Custom SvgIcon, or one from the Material UI icon library
});

// The same plugin as above, now with the extensions added
export const examplePlugin = createPlugin({
id: 'example',
extensions: [examplePage, exampleNavItem],
// We can also make routes available to other plugins.
routes: {
root: rootRouteRef,
},
});

我们在这里构建的是一种非常常见的插件类型。 它是一个提供单个页面的顶级工具,以及导航到该页面的方法。 页面组件的实现,在本例中是突出显示的ExamplePage它可以是一个简单的信息页面,也可以是一个包含多个子页面的完整应用程序。

通过将路由引用传递给插件,我们还为路由引用提供了外部访问权限routes这样,应用程序集成商就可以将不同插件的外部链接绑定到我们的插件页面。 您可以在外部路由参考节。

实用程序接口

另一种常用的扩展名是实用 API它们可以封装插件的共享功能片段,例如后端服务的 API 客户端。 您可以选择导出实用程序编程接口(Utility API)供其他插件使用,或者允许集成商用自己的实用程序编程接口替换您的实用程序编程接口。 有关如何在插件中定义和提供自己的实用程序编程接口(Utility API)的详情,请参阅 "如何在插件中定义和提供自己的实用程序编程接口(Utility API)"一节。创建实用程序应用程序接口.

我们在此展示的是一个仅在插件内部使用的简单实用程序编程接口的完整示例:

src/api.ts - Defining an interface, API reference, and default implementation
import { createApiRef } from '@backstage/frontend-plugin-api';

export interface ExampleApi {
getExample(): { example: string };
}

export const exampleApiRef = createApiRef<ExampleApi>({
id: 'plugin.example',
});

export class DefaultExampleApi implements ExampleApi {
getExample(): { example: string } {
return { example: 'Hello World!' };
}
}
src/components/ExamplePage.tsx - Using the API in our page component
import { useApi } from '@backstage/frontend-plugin-api';
import { exampleApiRef } from '../api';

export function ExamplePage() {
const exampleApi = useApi(exampleApiRef);

return (
<div>
<h1>Example Page</h1>
<p>Example: {exampleApi.getExample().example}</p>
</div>
);
}
in src/plugin.ts - Registering a factory for our API
import {
createApiFactory,
createApiExtension,
} from '@backstage/frontend-plugin-api';
import { exampleApiRef, DefaultExampleApi } from './api';

const exampleApi = createApiExtension({
factory: createApiFactory({
api: exampleApiRef,
deps: {},
factory: () => new DefaultExampleApi(),
}),
});

/* Omitted definitions for examplePage, exampleNavItem, and rootRouteRef. */

export const examplePlugin = createPlugin({
id: 'example',
extensions: [
exampleApi,
examplePage,
exampleNavItem,
],
routes: {
root: rootRouteRef,
},
});

插件特定扩展

有许多不同的插件,您可以通过扩展插件来扩展额外的功能。 其中一个插件是目录插件它可让您对组织中的软件进行编目,编目中的每个项目都有自己的页面,可填充与该编目实体相关的工具和信息。 在本例中,我们将探讨我们的插件如何提供这样的工具,以便在实体页面上显示。

in src/plugin.ts - An example entity content extension
import { createEntityContentExtension } from '@backstage/plugin-catalog-react';

// Entity content extensions are similar to page extensions in that they are rendered at a route,
// although they also have a title to support in-line navigation between the different content.
// Just like a page extension the content is lazy loaded, and you can also provide a
// route reference if you want to be able to generate a URL that links to the content.
const exampleEntityContent = createEntityContentExtension({
defaultPath: 'example',
defaultTitle: 'Example',
loader: () =>
import('./components/ExampleEntityContent').then(m => (
<m.ExampleEntityContent />
)),
});

export const examplePlugin = createPlugin({
id: 'example',
extensions: [
exampleEntityContent
exampleApi,
examplePage,
exampleNavItem,
],
routes: {
root: rootRouteRef,
},
});

ExampleEntityContent本身也是一个普通的 React 组件,您可以在其中实现任何您想要的功能。 要访问正在渲染内容的实体,您可以使用useEntity@backstage/plugin-catalog-react您可以在目录 React 库中查看其提供的 API 的完整列表。应用程序接口参考.

有关您可以为插件创建的不同类型扩展的完整列表,请参见扩展类型节。