ARTICLE DETAIL

资讯详情

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

Headlamp 插件开发教程:用 registerRoute 与 registerSidebarEntry 构建自定义页面与侧边栏导航

Headlamp 插件开发教程:用 registerRoute 与 registerSidebarEntry 构建自定义页面与侧边栏导航 Headlamp 插件开发教程用 registerRoute 与 registerSidebarEntry 构建自定义页面与侧边栏导航【免费下载链接】headlampA Kubernetes web UI that is fully-featured, user-friendly and extensible项目地址: https://gitcode.com/GitHub_Trending/he/headlamp导读本篇是 Headlamp 插件开发入门系列Getting Started的第三篇教程聚焦于一个核心问题如何在 Headlamp 中创建独立的自定义页面并通过侧边栏导航让用户发现这些页面。你将掌握registerRoute()注册路由、registerSidebarEntry()添加侧边栏条目、父子级子条目组织、图标配置以及 Home View 与 Cluster View 两种视图下导航的差异。学完本篇你就能为自己的插件搭建出结构清晰、可导航、可扩展的多页面 UI。前置准备承接 Tutorial 2 的插件工程本教程假设你已经完成了 Tutorial 2创建你的第一个插件手头有一个可运行的hello-headlamp插件并且 Headlamp 正在本地运行。如果你还没有运行环境的搭建经验可以参考 从源码运行。在 Tutorial 2 中我们通过registerAppBarAction()在 Headlamp 顶部的 App Bar 中放置了一个 Say Hello 按钮点击后弹出 alert。本教程将在这个基础上用registerRoute()创建一个真正的自定义页面让 App Bar 按钮从弹提示进化为跳转页面用registerSidebarEntry()把页面挂进左侧导航菜单。最终目标效果如下Sidebar (Home View): ├── [Clusters list] ├── My Plugin → expandable parent │ ├── Overview → /my-plugin │ └── Settings → /my-plugin/settings └── Plugin Docs → /plugin-docs预计耗时约 20 分钟。核心概念路由Route与侧边栏条目SidebarEntry在动手写代码之前先理解 Headlamp 插件扩展导航的两大 API。二者都从kinvolk/headlamp-plugin/lib导出见 插件 SDK 入口API职责registerRoute(routeSpec)把某个 React 组件注册到指定 URL 路径上形成可访问的页面registerSidebarEntry(entryProps)在左侧导航菜单中注册一个条目点击后跳转到指定 URL从源码层面看这两个函数最终都是向 Headlamp 的 Redux store 派发 action见 frontend/src/plugin/registry.tsx 与 frontend/src/plugin/registry.tsxregisterSidebarEntry({...})→store.dispatch(setSidebarItem({ name, label, url, parent, useClusterURL, icon, sidebar, entryType, sx }))registerRoute(routeSpec)→store.dispatch(setRoute(routeSpec))也就是说路由决定哪个 URL 显示什么组件侧边栏条目决定导航菜单里有什么、点了去哪二者通过sidebar字段路由侧与name字段条目侧建立关联。Route接口的完整字段定义可以查看 frontend/src/lib/router/Route.tsx包含path、exact、name、useClusterURLnoCluster已废弃、noAuthRequired、sidebar、component、hideAppBar、disabled、isFullWidth等SidebarEntry的完整字段定义见 frontend/src/components/Sidebar/sidebarSlice.ts包括name、label、parent、url、useClusterURL、icon、sidebar、entryType、sx等。后面的章节会逐一解释这些字段的用法。创建自定义页面第一步registerRoute在 Headlamp 中一个页面本质上就是一个被渲染到特定 URL 上的 React 组件。Step 1编写页面组件打开hello-headlamp插件的src/index.tsx用下面的内容替换import { registerRoute } from kinvolk/headlamp-plugin/lib; import { SectionBox } from kinvolk/headlamp-plugin/lib/CommonComponents; import { Typography } from mui/material; function WelcomePage() { return ( SectionBox titleWelcome to My Plugin Typography variantbody1 This is your first custom page! /Typography /SectionBox ); } registerRoute({ path: /my-plugin, sidebar: null, component: WelcomePage, useClusterURL: false, noAuthRequired: true, });这段代码在做什么代码作用registerRoute()告诉 Headlamp 在特定 URL 显示某个组件path: /my-plugin页面可访问的 URL 路径sidebar: null暂时不关联任何侧边栏条目后面会补上component: WelcomePage要渲染的 React 组件useClusterURL: false页面在/my-plugin访问而非集群专属 URLnoAuthRequired: true页面无需认证即可访问SectionBox来自kinvolk/headlamp-plugin/lib/CommonComponents。这个模块提供与 Headlamp 风格一致、开箱即用的 UI 组件后续教程还会用到NameValueTable、ResourceListView等。它的实现位于仓库的 frontend/src/components/common 目录下。Step 2手动访问页面保存文件后在浏览器中打开http://localhost:3000/my-plugin你应该能看到 Welcome 页面手动导航的问题页面能用了但存在一个明显的体验缺陷用户必须知道确切的 URL 才能访问。接下来我们逐步改进。连接 App Bar 按钮从 alert 到页面跳转还记得 Tutorial 2 中的 Say Hello 按钮吗现在让它直接导航到新页面而不是弹出 alert。更新src/index.tsximport { registerAppBarAction, registerRoute } from kinvolk/headlamp-plugin/lib; import { SectionBox } from kinvolk/headlamp-plugin/lib/CommonComponents; import { Button, Typography } from mui/material; function WelcomePage() { return ( SectionBox titleWelcome to My Plugin Typography variantbody1 This is your first custom page! /Typography /SectionBox ); } registerRoute({ path: /my-plugin, sidebar: null, component: WelcomePage, useClusterURL: false, noAuthRequired: true, }); function HelloButton() { return ( Button variantoutlined sizesmall href/my-plugin sx{{ color: inherit, borderColor: inherit, mx: 1 }} My Plugin /Button ); } registerAppBarAction(HelloButton /);与 Tutorial 2 的差异之前Tutorial 2之后Tutorial 3onClick{() alert(...)}href/my-plugin点击弹出 alert 提示点击跳转到自定义页面保存后把鼠标悬停在 App Bar 的My Plugin按钮上浏览器左下角会显示目标链接/my-plugin点击即可进入 Welcome 页面。不过你可能会注意到一个细节这个页面没有侧边栏。原因是我们使用了useClusterURL: false创建了一个脱离集群上下文的独立页面。下面进入本教程的重点——侧边栏导航。为什么侧边栏导航如此重要App Bar 按钮能用但存在硬伤App Bar 空间有限。如果插件有多个页面不可能为每个页面都加一个按钮。侧边栏——Headlamp 左侧的导航菜单——是标准解决方案它具备容纳多个条目的空间父子层级组织parent/child能力当前页面高亮highlight反馈用户始终知道自己在哪作为 Headlamp 标准导航模式的一致性体验。添加侧边栏条目registerSidebarEntry把页面挂进侧边栏需要两步注册条目并把路由的sidebar字段指向该条目。Step 1注册侧边栏条目并关联路由更新src/index.tsximport { registerAppBarAction, registerRoute, registerSidebarEntry } from kinvolk/headlamp-plugin/lib; import { SectionBox } from kinvolk/headlamp-plugin/lib/CommonComponents; import { Button, Typography } from mui/material; function WelcomePage() { return ( SectionBox titleWelcome to My Plugin Typography variantbody1 This is your first custom page! /Typography Typography variantbody2 sx{{ mt: 2, color: text.secondary }} Now accessible from the sidebar! /Typography /SectionBox ); } // 注册页面——注意 sidebar 现在指向我们的条目 registerRoute({ path: /my-plugin, sidebar: my-plugin, component: WelcomePage, useClusterURL: false, noAuthRequired: true, }); // 注册侧边栏条目 registerSidebarEntry({ name: my-plugin, label: My Plugin, url: /my-plugin, useClusterURL: false, }); // 保留 App Bar 按钮可选 function HelloButton() { return ( Button variantoutlined sizesmall href/my-plugin sx{{ color: inherit, borderColor: inherit, mx: 1 }} My Plugin /Button ); } registerAppBarAction(HelloButton /);新增内容详解registerSidebarEntry的选项属性作用name条目的唯一标识必须与路由中的sidebar匹配label侧边栏中显示的文本url点击后跳转的 URLuseClusterURL为false时 URL 保持/my-plugin为true默认时 URL 变为/c/:cluster/my-pluginregisterRoute的变化属性之前之后sidebarnullmy-plugin——把路由与侧边栏条目关联起来注意registerRoute还接受可选的name属性如name: My Plugin提供人类可读的名称可用于浏览器标签页标题。这也与仓库中的约定一致——在 frontend/src/lib/router/Route.tsx 中name被注释为Human readable name. Capitalized and short.人类可读、首字母大写且简短。关键约定registerRoute中的sidebar值必须与registerSidebarEntry中的name完全一致。这一关联带来三个效果当你停留在该页面时侧边栏条目被高亮Headlamp 据此决定显示哪个侧边栏无论你是点击侧边栏条目、点击 App Bar 按钮还是手动输入 URL侧边栏条目都会被正确选中。Step 2查看效果保存文件进入 Headlamp 首页http://localhost:3000/左侧侧边栏会看到新的My Plugin条目点击它进入 Welcome 页面。连接是双向的点击侧边栏条目 → 跳转到页面停留在页面 → 侧边栏条目高亮。添加图标Iconify 图标字符串没有图标的侧边栏条目看起来有些单调。Headlamp 使用Iconify作为图标体系通过字符串标识符即可使用数千个图标MDI 系列Material Design Icons。给registerSidebarEntry加上icon字段registerSidebarEntry({ name: my-plugin, label: My Plugin, url: /my-plugin, icon: mdi:new-box, useClusterURL: false, });保存后侧边栏条目旁边会出现一个 new新盒子图标。图标字符串的格式为mdi:图标名例如mdi:book-open-variant书本、mdi:comment-quote评论。在仓库的 Sidebar 示例插件 中可以看到icon: mdi:comment-quote、icon: mdi:hexagon等更多用法。从源码角度看icon字段的类型是 Iconify 的IconProps[icon]见 frontend/src/components/Sidebar/sidebarSlice.ts支持字符串标识或图标对象两种形式。创建子条目用parent组织层级随着插件功能增长你会希望把相关页面归组到父级条目之下。下面把插件扩展为父级 两个子条目的结构。Step 1更新插件代码用下面的完整版本替换src/index.tsximport { registerRoute, registerSidebarEntry } from kinvolk/headlamp-plugin/lib; import { SectionBox } from kinvolk/headlamp-plugin/lib/CommonComponents; import { Typography } from mui/material; // 页面组件 function OverviewPage() { return ( SectionBox titleOverview TypographyWelcome to the plugin overview!/Typography /SectionBox ); } function SettingsPage() { return ( SectionBox titlePlugin Settings TypographyConfigure your plugin settings here./Typography /SectionBox ); } // 注册路由 registerRoute({ path: /my-plugin, exact: true, name: Plugin Overview, sidebar: my-plugin-overview, component: OverviewPage, }); registerRoute({ path: /my-plugin/settings, name: Plugin Settings, exact: true, sidebar: my-plugin-settings, component: SettingsPage, }); // 注册父级侧边栏条目 registerSidebarEntry({ name: my-plugin, label: My Plugin, icon: mdi:new-box, url: /my-plugin, }); // 注册子级侧边栏条目 registerSidebarEntry({ parent: my-plugin, name: my-plugin-overview, label: Overview, url: /my-plugin, }); registerSidebarEntry({ parent: my-plugin, name: my-plugin-settings, label: Settings, url: /my-plugin/settings, });新增内容详解代码作用exact: true路由只做精确匹配而不是以该路径开头就匹配parent: my-plugin让 Overview 和 Settings 成为my-plugin的子条目父条目带url父条目本身可点击跳转到 Overview 页面子条目Overview 与 Settings 作为子菜单显示在 My Plugin 之下parent字段在SidebarEntry接口中的类型为parent?: string | null见 frontend/src/components/Sidebar/sidebarSlice.ts。当不指定parent或为null时条目出现在顶层。Step 2查看层级效果保存后侧边栏会呈现My Plugin () → 可展开的父级 ├── Overview → 点击跳转 /my-plugin └── Settings → 点击跳转 /my-plugin/settings点击 My Plugin 或 Overview 进入 Overview 页面点击 Settings 进入 Settings 页面。当停留在任一子页面时父条目会自动展开并高亮当前子条目Home View 与 Cluster View两种导航上下文Headlamp 存在两个主要上下文Home View首页视图——未选择集群时显示例如集群选择界面Cluster View集群视图——正在使用某个具体集群时显示。默认情况下侧边栏条目只会出现在 Cluster View 中。如果你希望某些导航在未连接集群时也可见需要显式指定sidebar: HOME。从源码看Headlamp 用DefaultSidebars枚举定义这两个内建侧边栏见 frontend/src/components/Sidebar/sidebarSlice.tsexport enum DefaultSidebars { HOME HOME, IN_CLUSTER IN-CLUSTER, }sidebar字段的类型是DefaultSidebars | string这意味着你既可以指向内建的HOME/集群侧边栏也可以创建一个全新的命名侧边栏仓库的 Sidebar 示例插件 就演示了通过sidebar: myplugin创建全新侧边栏并往其中添加条目的玩法。添加 Home View 条目下面注册一个无需集群即可访问的文档页面import { registerRoute, registerSidebarEntry } from kinvolk/headlamp-plugin/lib; import { SectionBox } from kinvolk/headlamp-plugin/lib/CommonComponents; import { Typography, Link } from mui/material; // 文档页面无需集群即可访问 function DocsPage() { return ( SectionBox titlePlugin Documentation Typography paragraph Welcome to the plugin documentation! /Typography Typography paragraph This page is accessible even when no cluster is selected. /Typography Link href/← Back to Clusters/Link /SectionBox ); } // 注册路由注意 useClusterURL: false registerRoute({ path: /plugin-docs, name: Plugin Docs, sidebar: { item: plugin-docs, sidebar: HOME, }, component: DocsPage, useClusterURL: false, noAuthRequired: true, }); // 在 HOME 侧边栏中注册条目 registerSidebarEntry({ name: plugin-docs, label: Plugin Docs, url: /plugin-docs, icon: mdi:book-open-variant, sidebar: HOME, useClusterURL: false, });关键差异属性作用sidebar: { item, sidebar }在registerRoute中指定要高亮的侧边栏条目以及它属于哪个侧边栏sidebar: HOME在registerSidebarEntry中把条目放入首页侧边栏useClusterURL: falseURL 不含/c/:cluster/前缀noAuthRequired: true页面无需认证即可访问Route.sidebar字段支持三种形态string | null | { item, sidebar }见 frontend/src/lib/router/Route.tsx其中对象形态允许你同时指定要激活的条目名与所属侧边栏。验证保存文件回到首页点击 Headlamp Logo 或访问http://localhost:3000/在侧边栏中找到 Plugin Docs带书本图标点击进入文档页面完整示例Cluster View Home View 组合下面是一份完整的src/index.tsx同时演示集群视图与首页视图导航import { registerRoute, registerSidebarEntry } from kinvolk/headlamp-plugin/lib; import { SectionBox } from kinvolk/headlamp-plugin/lib/CommonComponents; import { Typography, Link } from mui/material; // Cluster View Pages function OverviewPage() { return ( SectionBox titlePlugin Overview TypographyWelcome to My Plugin! This page is cluster-specific./Typography /SectionBox ); } function SettingsPage() { return ( SectionBox titlePlugin Settings TypographyConfigure your plugin settings here./Typography /SectionBox ); } // Home View Pages function DocsPage() { return ( SectionBox titlePlugin Documentation Typography paragraph This page is accessible without selecting a cluster. /Typography Link href/← Back to Clusters/Link /SectionBox ); } // Cluster View Routes Sidebar registerRoute({ path: /my-plugin, sidebar: my-plugin-overview, component: OverviewPage, exact: true, }); registerRoute({ path: /my-plugin/settings, sidebar: my-plugin-settings, component: SettingsPage, exact: true, }); registerSidebarEntry({ name: my-plugin, label: My Plugin, icon: mdi:new-box, url: /my-plugin, }); registerSidebarEntry({ parent: my-plugin, name: my-plugin-overview, label: Overview, url: /my-plugin, }); registerSidebarEntry({ parent: my-plugin, name: my-plugin-settings, label: Settings, url: /my-plugin/settings, }); // Home View Routes Sidebar registerRoute({ path: /plugin-docs, component: DocsPage, useClusterURL: false, noAuthRequired: true, sidebar: { item: plugin-docs, sidebar: HOME, }, }); registerSidebarEntry({ name: plugin-docs, label: Plugin Docs, url: /plugin-docs, icon: mdi:book-open-variant, sidebar: HOME, useClusterURL: false, });更进阶的能力从示例插件中挖掘仓库中的 Sidebar 示例插件 是官方提供的完整参考实现它展示了本教程之外的多种进阶玩法值得通读entryType: subheader注册不可点击的分组标题条目配合sx自定义样式用于在侧边栏中给条目分组见 示例插件第 84-93 行registerSidebarEntryFilter/registerRouteFilter动态移除或修改侧边栏条目与路由例如在进入某个页面时用useEffect隐藏特定条目见 示例插件第 216-228 行registerHomeSidebarEntryFilter过滤 HOME 侧边栏条目见 示例插件第 295 行useClusterURL: falsehideAppBar: true创建完全脱离集群前缀、甚至隐藏顶部 App Bar 的独立页面见 示例插件第 257-282 行。这些能力对应的底层实现同样位于 frontend/src/plugin/registry.tsxregisterSidebarEntryFilter派发setSidebarItemFilterregisterRouteFilter派发setRouteFilter返回null即删除条目/路由返回可修改的条目/路由则保留。Troubleshooting常见问题排查侧边栏条目不出现检查集群上下文未指定sidebar: HOME的条目只会在选中集群后出现确保已选择集群才能看到集群视图条目。检查拼写registerSidebarEntry中的name必须与registerRoute中的sidebar完全一致。确认插件已加载进入 Settings → Plugins确认你的插件已列出且已启用。页面 404 或空白检查 URL 模式集群视图 URL 格式/c/:cluster/你的路径首页视图 URL 格式/你的路径。检查useClusterURL路由中为useClusterURL: false时不带集群前缀访问为useClusterURL: true默认时URL 需要包含集群前缀。侧边栏条目不高亮确保sidebar与name匹配// 这两处必须一致 registerRoute({ path: /my-plugin, sidebar: my-plugin, // ← 这个... component: MyPage, }); registerSidebarEntry({ name: my-plugin, // ← ...必须与这个一致 label: My Plugin, url: /my-plugin, });子条目不显示检查parent引用registerSidebarEntry({ name: my-plugin, // ← 父条目 name label: My Plugin, }); registerSidebarEntry({ parent: my-plugin, // ← 必须与父条目 name 一致 name: my-plugin-child, label: Child Entry, url: /my-plugin/child, });parent的值必须与父条目的name精确一致否则层级关系无法建立。下一步通过本教程你已经掌握了 Headlamp 插件导航的完整基础✅ 用registerRoute()创建自定义页面✅ 用registerSidebarEntry()添加侧边栏条目✅ 用 Iconify 图标提升视觉效果✅ 用parent组织父子层级✅ 区分 Home View 与 Cluster View 两种导航上下文目前页面还是静态的。接下来的教程将让页面活起来Tutorial 4使用 Kubernetes 数据——用内置资源类和 ApiProxy 获取集群信息、命名空间等数据见 working-with-kubernetes-dataTutorial 5进阶 Kubernetes 交互——创建自定义资源类、通过 API 修改资源见 working-with-kubernetes-data-advanced。Quick Reference快速参考registerRoute 选项registerRoute({ path: /my-path, // URL 路径必填 sidebar: sidebar-name, // 要高亮的侧边栏条目必填 component: MyComponent, // React 组件必填 useClusterURL: true, // 是否包含 /c/:cluster/ 前缀默认: true noAuthRequired: false, // 是否允许未认证访问默认: false exact: true, // 精确路径匹配默认: true name: route-name, // 可选的路由标识 });registerSidebarEntry 选项registerSidebarEntry({ name: unique-name, // 唯一标识必填 label: Display Label, // 侧边栏显示的文本必填 url: /my-path, // 点击跳转的 URL icon: mdi:icon-name, // Iconify 图标字符串 parent: parent-name, // 父条目 name用于子条目 sidebar: HOME, // HOME 表示首页视图省略则为集群视图 useClusterURL: true, // 是否包含 /c/:cluster/ 前缀默认: true });URL 模式速查上下文模式示例集群视图/c/:cluster/你的路径/c/minikube/my-plugin首页视图/你的路径/plugin-docs【免费下载链接】headlampA Kubernetes web UI that is fully-featured, user-friendly and extensible项目地址: https://gitcode.com/GitHub_Trending/he/headlamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表