背景
灵感来源于最近看到一个 aliyun grafana 托管版的客户通过 html 插件弄了个高度定制的自定义大盘,客户在使用这个 html 插件时手写了大量 dom,然后再用原生 js 手动操作 dom 展示数据,实在过于繁琐,没个前端 20 年功力感觉真搞不出来,另外这个 HTML 插件用的还是老的 angular 框架,grafana 官方在新开源版本也不再支持了。
那有没有一种方法可以让我用 “现代一点” 的 jsx 语法写 react 组件来自定义 grafana 面板呢?
How?
熟悉低代码的同学可能早就想到了,搞个 jsx 实时渲染组件,核心就三步:
- 用 monaco 提供代码编辑器
- 用 babel 提供转码编译
- 执行渲染
Create grafana plugin
在 grafana 中所有可扩展形态都被设计成插件,面板也是一个插件,我们用 grafana 官方提供好的 cli 工具链创建一个 panel plugin,启动调试会起一个完整的 grafana 容器,需要提前装好 docker。
代码编辑器
代码编辑器在 grafana 官方开源组件库 @grafana/ui 已经有提供好的现成组件,因为 grafana 提供 dark/light 俩套主题,方便适配主题样式,直接用官方提供的 code editor 组件就好(其实这个组件底层也是用的 monaco 内核):
import { CodeEditor as GrafanaCodeEditor } from '@grafana/ui';
因为 monaco 本身不支持 jsx 语法,还需要加一些黑魔法配置:
const onBeforeEditorMount = (monaco: typeof monacoType) => { monaco.languages.typescript.typescriptDefaults.setCompilerOptions({ target: monaco.languages.typescript.ScriptTarget.Latest, allowNonTsExtensions: true, moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, module: monaco.languages.typescript.ModuleKind.CommonJS, noEmit: true, esModuleInterop: true, jsx: monaco.languages.typescript.JsxEmit.React, reactNamespace: "React", allowJs: true, typeRoots: ["node_modules/@types"], }); monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({ noSemanticValidation: false, noSyntaxValidation: false, }); monaco.languages.typescript.typescriptDefaults.addExtraLib( // Type definitions for React 16.9 ReactTypes, ); }
实时编译
babel 提供了一个可在浏览器执行的版本 @babel/standalone,可以在浏览器环境实时编译 es6 代码,使用非常方便。
const compiled = Babel.transform(source, { presets: ['env', 'react'] }).code;
Grafana 插件最终构建出来的是 amd 产物,实际使用时会被 grafana 主应用使用 systemjs 加载进来,类似于微前端的加载方式,因为 @babel/standalone npm 包实际提供的一份 umd 格式产物,导致如果我们在项目中直接 require npm 包的方式,会由于不同 grafana systemjs 配置不同,只能在开源 grafana 最新的 v10.0.x 版本正常使用,所以推荐使用 @babel/standalone 还是用 cdn js 的方式引入。
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
执行渲染
最后一步,拿到 babel 编译后的代码之后,通过 new Function 的方式起一个执行容器执行渲染。
为了方便更好的构建自定义 react 组件,这里函数组件参数透传除了常用的 panel props 如 数据源 data,还透传了 @grafana/ui 完整的组件库,可以在预置好的函数内直接使用。
结语
Jsx 组件在 aliyun grafana 共享版已经发布了,初版仅仅够用还比较简陋
如果想要部署到自托管的的 grafana 上,代码已开源,欢迎使用: https://github.com/foginn/grafana-jsx-panel