ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

30分钟开发你自己的Hestia小组件:BaseApp与Registry机制完全教程

30分钟开发你自己的Hestia小组件:BaseApp与Registry机制完全教程

【免费下载链接】hestia-core

A grid-based, modular dashboard built entirely from HTML, CSS, and JS with the ability to create your custom API integrations.

项目地址:https://gitcode.com/gh_mirrors/he/hestia-core
点击查看免费下载

Hestia 是一个纯 HTML、CSS、JavaScript 构建的网格化仪表盘(Dashboard),内置时钟、天气、Glances 监控等组件,也支持接入 Pi-hole、Jellyfin 等家庭服务 API。它最吸引人的设计是:任何人都能用自己的 API 集成能力扩展它——因为一个自定义小组件(Widget)只需继承BaseApp类、调用一次registry.register注册即可上线,全程无需构建工具,也没有后端依赖。

一、为什么 30 分钟就够?先看懂这套组件机制

Hestia 的组件开发只有三步闭环:

  1. 写类:继承 baseApp.js 导出的BaseApp,实现render方法返回 HTML 字符串;
  2. 注册:调用 registry.js 单例的register(type, AppClass, metadata),登记组件类型、默认尺寸和设置表单;
  3. 导入:在 appIndex.js 中加一行import,组件就会出现在仪表盘的"Add New App"列表里。

整个流程不涉及打包、不涉及路由,所有逻辑都是原生 ES Module。下面拆开这两个核心文件。

二、两大核心:BaseApp 与 Registry

2.1 BaseApp:每个组件必须遵守的"最小契约"

打开 js/apps/baseApp.js,你会发现它只有 10 行代码:

export class BaseApp { constructor() { // Shared logic can go here later } // All apps must implement this async render(appData) { throw new Error("Render method not implemented"); } }

它定义了组件生命周期的两个方法:

方法是否必须作用
async render(app)✅ 必须实现返回组件卡片的 HTML 字符串,app对象包含该组件的位置、尺寸和用户保存的设置(app.data)
onMount(el, app)⭕ 可选HTML 插入 DOM 后执行,适合绑定事件、启动定时器、请求 API

这种"先渲染静态 HTML,再挂载动态逻辑"的分离设计,是 Hestia 组件模型的关键:render负责长什么样,onMount负责做什么。

2.2 Registry:带样式自动注入的组件注册表

registry.js 导出了一个全局单例registry,核心是register方法:

registry.register('my-app', MyApp, { label: 'My Cool App', // "Add App" 列表里显示的名称 category: 'static', // (可选)分类 defaultSize: { cols: 2, rows: 1 }, // 网格默认占用尺寸 settings: [ /* 设置项数组 */ ], css: `.my-app { ... }` // 组件专属样式,注册时自动注入 <style> });

register做了两件关键的事(见 registry.js#L8-L21):

  • 把Class和metadata存入this.apps[type]。注意:如果 type 已存在会直接忽略,不会覆盖,这是防止命名冲突的保护;
  • 如果元数据里带了css,立即通过injectStyles注入到页面<head>,且每个组件只注入一次。

注册完成后,网格系统和编辑面板都从注册表取数:

  • grid.js 中的mountAppContent会用registry.get(app.subtype)找到组件类,实例化后依次调用render→ 插入 DOM →onMount;
  • appEditor.js 用registry.getAll()渲染"Add New App"弹窗,并根据settings数组自动生成设置表单——你不用手写任何表单 DOM。

三、四步开发你的第一个组件

以官方示例 helloWorld.js 为蓝本,完整流程如下。先获取源码:

git clone https://gitcode.com/gh_mirrors/he/hestia-core

第 1 步:创建文件js/apps/myApp.js

第 2 步:继承 BaseApp 实现 render

import { BaseApp } from "./baseApp.js"; import { registry } from "../registry.js"; class MyHello extends BaseApp { async render(app) { const text = app.data.text || "Hello, Hestia!"; return ` <div class="app-content app-type-myhello"> <h2>${text}</h2> </div>`; } }

app.data里保存着用户在设置面板填写的所有值(以及卡片背景色、文字色),记得对每个字段提供默认值兜底。

第 3 步:注册组件

registry.register('my-hello', MyHello, { label: 'My Hello', category: 'static', defaultSize: { cols: 2, rows: 1 }, settings: [ { name: 'text', label: 'Greeting Message', type: 'text', placeholder: 'e.g. Hello, World!' } ], css: ` .app-type-myhello { display: flex; align-items: center; justify-content: center; height: 100%; color: inherit; } ` });

settings数组支持的type包括:text、select(需配options)、textarea、image-source(图片链接或上传,自动存入 IndexedDB)。每个字段的name就是app.data里的键名,label会原样显示在设置表单中。

第 4 步:在 appIndex.js 中导入

在 js/apps/appIndex.js 末尾加一行:

import './myApp.js';

刷新页面,点击工具栏的"+"(Add New App),你的组件就出现在列表里了。添加后卡片会自动寻找空闲网格位置(findEmptySlot逻辑在 grid.js),双击标题还能重命名。

上图正是默认仪表盘里 Image Frame 组件加载 constants.js 中预设素材的渲染效果——它本身就是一个注册在注册表里的组件。

四、进阶:让组件动起来

静态文本只是起点。参考 clockApp.js,在onMount里可以操作 DOM、启动定时器:

onMount(el, app) { const timeEl = el.querySelector('.clock-time'); const update = () => { // 安全检查:卡片被删除后停止运行,避免内存泄漏 if (!el.isConnected) { clearInterval(intervalId); return; } timeEl.innerText = new Date().toLocaleTimeString(); }; update(); const intervalId = setInterval(update, 1000); }

两个值得学习的关键点:

  • el.isConnected检查:用户删除卡片时 DOM 节点会被移除,定时器必须随之停止,否则会出现"幽灵更新";
  • el是整张卡片的元素,用el.querySelector查找内部节点。

如果要接 API(轮询数据、绘制图表),官方 Glances 组件是最好的模板:glancesApp.js 展示了setInterval轮询 + 错误兜底(404/离线提示)+ 按指标切换子模块(逻辑拆在 js/apps/glances/ 目录下)的完整写法。

五、常见坑点清单

坑现象解法
忘记在 appIndex.js 导入组件不显示在 Add App 列表加import './myApp.js';,该文件被 main.js 间接加载
type 命名冲突注册被静默忽略打开控制台看[Registry] Registered:日志确认
忘记写默认值新卡片显示undefinedapp.data.xxx一律用|| '默认值'兜底
onMount 里泄漏定时器删除卡片后 CPU 持续占用每次 tick 前检查el.isConnected
样式污染其他组件全局 class 名撞车用.app-type-xxx作用域前缀(CSS 已按组件隔离注入)

六、关键文件速查

文件职责
js/apps/baseApp.js组件基类,定义 render 契约
js/registry.js注册表单例与样式注入
js/apps/appIndex.js组件导入总入口
js/apps/helloWorld.js最简完整示例(推荐先读)
js/apps/clockApp.jsonMount + 定时器示例
js/apps/glancesApp.jsAPI 轮询 + 图表 + 错误处理模板
js/grid.js网格渲染、组件挂载、空闲位置查找
js/ui/appEditor.jsAdd/Edit App 弹窗与设置表单生成
index.html页面骨架与工具栏按钮

从render返回一段 HTML,到registry.register登记一个类型,Hestia 的 BaseApp + Registry 机制把"组件开发"压缩成了三件事。照着 helloWorld.js 抄一遍、换上自己的数据源,你的第一个自定义小组件 30 分钟就能上线。🚀

【免费下载链接】hestia-core

A grid-based, modular dashboard built entirely from HTML, CSS, and JS with the ability to create your custom API integrations.

项目地址:https://gitcode.com/gh_mirrors/he/hestia-core
点击查看免费下载
上一篇:Flink 调试窗口与事件时间:监控 currentInputWatermark 指标与处理迟到的散乱事件
下一篇:SpaceX-API v5 单次发射查询指南:深入解析 GET /v5/launches/:id 端点

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

返回列表