Budibase bbui 组件库开发指南:Svench 工作流、组件规范与源码结构解析
2026/9/10 14:07:05 网站建设 项目流程

Budibase bbui 组件库开发指南:Svench 工作流、组件规范与源码结构解析

【免费下载链接】budibaseAI agents, automations and apps that run your operations. Model agnostic.项目地址: https://gitcode.com/GitHub_Trending/bu/budibase

bbui(@budibase/bbui)是 Budibase 组织内所有前端项目共享的 Svelte 组件库,Builder、Client 等界面中的按钮、表格、弹窗、通知等基础组件均来自该包。本篇指南以 packages/bbui/README.md 为核心,完整讲解如何安装与启动组件开发环境、如何基于 Svench 的“变体(Variant)”工作流创建新组件、组件与样式使用规范,并结合仓库源码剖析 bbui 的导出架构、CSS 自定义属性体系与典型组件实现,帮助你快速上手为 Budibase 贡献 UI 组件或在项目中正确消费这套组件库。

一、bbui 是什么:一个包承载 Budibase 全部公共组件

README 开篇即点明 bbui 的定位:一个处理 Budibase 组织内所有公共组件的包。从仓库结构看,它位于 packages/bbui 目录,是一个独立的 npm 包,包名为@budibase/bbui(见 packages/bbui/package.json),采用 MPL-2.0 许可证。

它在项目中的实际地位可以从消费方验证:packages/builder/package.json中声明了对"@budibase/bbui": "*"的依赖,Builder 的App.svelte、ContextMenu、自动化流程画布等大量页面组件都从 bbui 导入组件。换句话说,bbui 是整个 Budibase 前端一致性的基石——统一的按钮、表单、菜单、弹窗、通知全部出自这一个包,而不是各项目各自维护一套 UI。

从依赖配置还能看出它的技术选型:底层大量复用 Adobe 的Spectrum CSS组件样式(@spectrum-css/button@spectrum-css/table@spectrum-css/modal等 30 余个包),并依赖dayjs(日期处理)、nanoid(ID 生成)、svelte-portal(弹层挂载)、sanitize-html(富文本净化)、easymde(Markdown 编辑)、atrament(手写签名)等实用库。

二、安装与启动:三步进入 Svench 开发环境

README 给出的安装步骤非常简洁,共三步:

  1. Clone 仓库
  2. 执行npm install
  3. 执行npm run svench

README 特别标注了一条重要提示:yarn 不可用(yarn won't work!)。原因是 bbui 通过 packages/bbui/vite.config.mjs 中的 alias 将@budibase/shared-core@budibase/types直接解析到 monorepo 内的兄弟包源码目录(../shared-core/src../types/src),同时package.json中又以"*"通配版本声明了对@budibase/shared-core@budibase/string-templates的内部依赖,这类工作区内部引用依赖 npm 的链接与解析行为,使用 yarn 容易产生包解析不一致的问题,因此请严格遵循 README 建议使用 npm。

由于npm run svench命令需要 Svench 依赖与源码编译,完整的本地运行需要先按仓库 README.md 的引导安装整个 monorepo 的依赖。启动后,Svench 会在浏览器中打开组件预览页,左侧是组件树,右侧是每个组件的“变体”渲染结果,并支持 HMR(热模块替换)——修改组件源码后页面即时刷新,这是比传统文档更高效的开发体验。

三、基于 Svench 的组件创建工作流:五步从零到发布

README 给出了创建新组件的标准工作流,这是本文最核心的实操部分,完整步骤如下:

  1. 创建组件文件:如Headline.svelte,在src下对应目录(如src/Headline/)编写 Svelte 组件;
  2. 创建 Svench 文件:同名创建Headline.svench,作为该组件的“活文档”;
  3. 构建组件并为 Svench 文件添加变体:在.svench中通过<View>声明组件的不同状态与尺寸组合,边开发边预览;
  4. src/index.ts中重新导出:让组件对包的使用者可见。README 中写的是src/index.js,而从当前仓库源码看,实际的统一出口文件是 packages/bbui/src/index.ts,导出时以export { default as Headline } from "./Headline/Headline.svelte"的形式追加即可;
  5. 发布并更新主项目中的包版本,让 Builder 等消费方拿到新组件。

仓库中现存的两个 Svench 文件可以作为参照:packages/bbui/src/Button/Button.svench 与 [packages/bbui/src/Drawer/Drawer.svench]。以 Button 为例,它的结构清晰地展示了 Svench 的用法:

<script> import { View } from "svench"; import Button from "./Button.svelte"; import Icon from "../Icons/Icon.svelte"; </script> <View name="Primary"> <Button primary on:click={() => alert('Clicked!')}>Default</Button> </View> <View name="Disabled"> <Button primary disabled on:click={() => alert('Clicked!')}>Disabled</Button> </View> <View name="use Knobs" knobs={{ primary: true, secondary: false, disabled: false, text: false }} let:knobs> <Button {...knobs} on:click={() => alert('Clicked!')}>Knooby</Button> </View>

要点解读:

  • 每个<View name="...">是一个变体,对应预览页中的一个展示块,名称即变体的语义标签(如 Primary、Secondary、Disabled);
  • 变体内可以自由组合组件与容器,甚至为暗色背景单独包裹div来验证translucent等场景;
  • knobs机制允许在预览页动态切换组件 props,免去为每个参数组合手写变体。

这套“组件 + 同名 Svench 文件”的约定,让每个组件的使用示例与源码天然同仓、同步演进,既是开发工具也是文档。

四、组件开发四准则:从规范到源码印证

README 的 Guidelines 部分给出了四条组件制作准则,每一条都能在现有源码中找到对应实践:

1. 思考可复用性(Re-usability)

组件应该通用、无业务假设。例如 packages/bbui/src/Table/Table.svelte 接收泛型数据O[]schema,把“渲染哪些列、是否可排序、是否可编辑”全部参数化,而不是绑定任何具体的数据结构。

2. 使用样式表中的 CSS 自定义属性(变量)

所有颜色、间距、字体、圆角都应取自全局样式表 packages/bbui/src/bbui.css,而不是在组件里写死硬编码值。这份样式表在:root中定义了完整的设计令牌体系,稍后第五节详述。

3. 优先转发事件,而非使用回调(callback)

README 的原话是“Opt to forward events (<button on:click>for example) rather than using callbacks”。以 packages/bbui/src/Button/Button.svelte 为例,组件内部用createEventDispatcher声明click事件,然后在<button on:click|preventDefault=...>dispatch("click"),使用者通过on:click消费,与原生 DOM 事件习惯一致;同时disabled时直接短路不发事件。Table.svelte同样声明了clicksorteditcolumneditrow四个转发事件。

4. 避免给组件最外层容器加 margin

组件的尺寸与位置应由父容器决定,组件自身不携带外边距,以保证任意布局下都能无缝拼接。README 的“使用组件”一节对此有更细的布局建议(见第六节)。

五、样式令牌体系:bbui.css 里的 CSS 自定义属性

bbui.css是 bbui 的设计基础,理解它才能真正用好这套组件库。文件在:root中声明的变量大致可分为几类:

品牌色

--bb-coral: #ff4e4e; --bb-indigo: #6e56ff; --bb-lime: #ecffb5; --bb-forest-green: #053835; --bb-beige: #f6efea;

语义色阶(每种颜色 100–950 共十档,如--color-brand-500: #386cf4--color-red-500: #b4564b--color-green-600: #788c5d--color-purple-600: #585392等),以及一组便捷短变量:--grey-1--grey-9--blue/--blue-dark--red/--red-dark--yellow--orange--green--purple及其-light变体。

字号与字体

--font-sans: "Inter", -apple-system, BlinkMacSystemFont, Segoe UI, ...; --font-mono: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --font-size-xs: 0.75rem; /* 到 --font-size-xl: 1.3rem */ --heading-font-size-s: 1.12rem; /* 到 --heading-font-size-xl: 3rem */

间距与圆角

--spacing-xs: 0.25rem; /* 到 --spacing-xl: 1.25rem */ --layout-xs: 1.25rem; /* 到 --layout-xl: 4rem */ --border-radius-xs: 0.125rem; /* 到 --border-radius-xl: 100rem */

边框

--border-black: 2px var(--ink) solid; --border-grey: 1px var(--grey-4) solid; --border-light: 1px var(--grey-3) solid;

文件后半部分还对 Spectrum CSS 做了两件事:一是通过.spectrum选择器把 Spectrum 的字号、行高等维度统一覆盖为 bbui 的变量(大量!important以确保优先级);二是定义了.spectrum--darkest.spectrum--dark.spectrum--light三套主题下的--drop-shadow--spectrum-global-color-*-100色值覆盖,实现明暗主题切换。组件源码中大量使用这些变量,例如Button.svelte的样式中gap: var(--spacing-s)color: var(--spectrum-global-color-blue-600)

六、使用组件与样式指南:props、变量与布局

README 对“消费组件”给出了三条实用建议,值得逐条展开:

  1. 熟悉组件上已有的 props:如果缺少关键能力,直接提 PR 补上,而不是在业务侧包一层 hack。以 Button 为例,Button.svelte 的 props 相当完备:type(button/submit/reset)、disabledsize(XXS–XXXL)、ctaprimarysecondarywarningoverBackgroundquieticon/iconColor/iconWeight/iconSize(内置图标)、activetooltip/tooltipPosition(悬浮提示,借助 AbsTooltip 包裹实现)、newStylesref(绑定底层<button>元素)等;
  2. 利用样式表中的 CSS 自定义属性,避免硬编码值:业务代码中需要微调颜色、间距时应引用var(--blue)var(--spacing-l)这类令牌;
  3. 组件没有 margin,间距由布局层负责:README 明确指出,正确的做法是使用CSS Grid +grid-gap,或在外层容器上设置padding/margin,具体方案视场景而定。这正是许多组件库“组件内聚、间距外置”的通用原则,保证任何排列组合下视觉节奏一致。

七、从 src/index.ts 看 bbui 的导出架构

packages/bbui/src/index.ts 是包的统一出口(package.jsonexports字段同时提供了dist/bbui.mjs与源码svelte: ./src/index.ts两条解析路径),它清楚地展示了组件库的全景分类:

  • 表单组件(Form)CheckboxComboboxDatePickerDateRangePickerDropzoneEnvDropdownFieldLabelFileCollapsibleSearchInputInputDropdownMultiselectPillInputRadioGroupRichTextFieldSearchSelectSliderStepperTextAreaTimeFieldToggle
  • 核心表单组件(Form/Core):通过export * from "./Form/Core"导出,见 packages/bbui/src/Form/Core/index.ts,包含CoreCheckboxCoreComboboxCoreDatePickerCoreMultiselectCoreSignature(签名板)、CoreSliderCoreSwitchCoreTextField等——这套 Core 系列是与业务数据绑定无关的“纯”表单控件,供客户端运行时渲染使用;
  • FancyForm:更复杂组合型表单组件集合;
  • 通用组件AccordionActionButtonActionMenuAvatarBadgeBannerButtonButtonGroupColorPickerDrawerIconIconPickerInlineAlertMarkdownEditor/MarkdownViewerMenu(含MenuItem/MenuSection/MenuSeparator)、ModalNotificationPaginationPhosphorIconPickerPopoverProgressBar/ProgressCircleStatusLightSwitcherTableTabs/TabTags/Tag、四款TooltipTreeView等;
  • 表格渲染器(Renderers)BoldRendererCodeRendererInternalRenderer,配合 Table 的单元格自定义渲染;
  • 排版(Typography)BodyCodeDetailHeading
  • Actions(Svelte actions)autoResizeTextAreaclickOutsidepositionDropdown
  • Storesbanner/BANNER_TYPES横幅 store,以及createNotificationStore/notifications通知 store;
  • Helpers 与类型export * as Helpers from "./helpers"汇总工具函数,export type * from "./types"导出公共类型。

vite.config.mjs表明包以ES Module 库模式构建:入口为src/index.ts,产物为dist/bbui.mjs,并通过vite-plugin-css-injected-by-js将 CSS 注入 JS,消费方无需手动引入样式文件。

八、典型组件与工具源码解析

通知系统:Notifications Store

packages/bbui/src/Stores/notifications.ts 实现了一个基于 Sveltewritable的通知队列:createNotificationStore()返回sendinfoerrorwarningsuccess等快捷方法;默认 3 秒自动消失(NOTIFICATION_TIMEOUT = 3000),error默认不自动关闭;支持action/actionMessage(操作按钮)、wide(宽屏布局)、blockNotifications(防抖屏蔽,避免高频重复弹窗)。模块级导出的notifications单例可直接在任意 Svelte 组件中$notifications订阅,配合NotificationDisplay渲染。

点击外部:clickOutside Action

packages/bbui/src/Actions/clickOutside.ts 是一个全局单例实现的 Svelte action,用于实现“点击组件外部触发回调”。它的实现有几个值得注意的细节:

  • 维护一个clickHandlers数组,通过mousedown记录候选目标、mouseup时校验按下与抬起目标一致才触发,避免“拖选文本”误触发;
  • 内置ignoredClasses(如.spectrum-Menu)与conditionallyIgnoredClasses(如.spectrum-Underlay.drawer-wrapper.spectrum-Popover),点击菜单内部不会误关弹层;
  • 支持data-ignore-click-outside="true"属性显式忽略;
  • 通过window.blur检测 iframe 点击导致的失焦;
  • opts.anchor用于 Popover 这类通过 Portal 渲染在 DOM 根部的组件,指定“真实锚点元素”以正确判断来源。

弹窗与叠加层:Modal + overlayStack

packages/bbui/src/Modal/Modal.svelte 通过svelte-portal将弹窗挂载到文档根部,并提供ModalAPIshow/hide/toggle/cancel)与fixedinlinedisableCancelcloseOnOutsideClickautoFocusbeforeClose等 props。多个弹窗/弹出层同时存在时,由 overlayStack 维护叠加顺序,z-index 按BASE_Z_INDEX + stackIndex递增,并用setContext(Context.PopoverRoot, ...)保证弹窗内部的 Popover 渲染在弹窗容器内而非全局根节点——这正是“组件可复用”在复杂交互场景下的工程化体现。取消来源通过 constants.ts 中的ModalCancelFrom枚举(关闭按钮/取消按钮/ESC/外部点击)区分。

工具函数:helpers.ts 与 ids.ts

packages/bbui/src/helpers.ts 提供了deepGet/deepSet(支持a.b.c点路径,且“带点键名”优先于嵌套路径)、uuid(DOM 安全的 UUID,首位固定字母)、cloneDeepcopyToClipboard(优先 Clipboard API,非安全上下文回退 textarea 方案)、日期系列工具(parseDate/stringifyDate/getDateDisplayValue,处理 enableTime/timeOnly/时区忽略等 schema 标志)、hexToRGBA/rgbToHex颜色转换,以及两个大型图标映射表AppIconMapSpectrumIconMap(将 Spectrum 图标名映射到 Phosphor 图标,供PhosphorIconPicker等使用)。ID 生成则依赖 packages/bbui/src/utils/ids.ts,基于nanoid生成 9 位 ID。

九、测试与后续规划

README 的 TODO 部分记录了两项计划:完善文档体系(“Figure out a good documentation situation”)与引入测试套件(候选方案为基于 Playwright 的 E2E 测试)。从当前仓库看,测试基础设施已经就位:package.json提供了vitest runnpm test)与 watch 模式脚本,vite.config.mjs中的test配置(globals: true,匹配src/**/*.test.*src/**/*.spec.*)也已就绪,packages/bbui/src/helpers.test.ts 便是现存单元测试示例。组件文档目前主要依赖 Svench 变体页承担“活文档”职能,这也解释了 README 将文档建设列为 TODO 的原因。

十、结语:把 bbui 当作 Budibase 前端开发的“第一课”

对于希望为 Budibase 贡献代码的开发者,bbui 是绝佳的切入点:组件小而独立、有清晰的开发预览环境、有明确的编写规范,改动的影响面可控。核心实践可浓缩为四句话:用 npm 安装、用 Svench 预览、组件转发事件不背回调、样式一律走 CSS 变量且外层不加 margin。在此基础上,通过 src/index.ts 了解组件全貌、对照 Button.svench 学习变体写法、阅读 notifications.ts 与 clickOutside.ts 学习进阶实现,即可快速具备“看懂、会用、能扩展”bbui 的能力。

【免费下载链接】budibaseAI agents, automations and apps that run your operations. Model agnostic.项目地址: https://gitcode.com/GitHub_Trending/bu/budibase

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询