amis HBox 水平布局组件详解:从 JSON 配置到 Flex 实现原理
【免费下载链接】amis前端低代码框架,通过 JSON 配置就能生成各种页面。项目地址: https://gitcode.com/GitHub_Trending/am/amis
HBox 是 amis 低代码框架内置的水平布局渲染器,通过type: "hbox"一条 JSON 配置即可将多个子渲染器横向排列为等宽或自定义宽度的多列布局。本文以 docs/zh-CN/components/hbox.md 为骨架,结合 HBox 渲染器源码、布局样式 与 单元测试,完整讲解 HBox 的属性表、列集合写法、间距与对齐控制,以及底层 Flex 布局的实现细节。
基本用法
HBox 使用columns数组描述列集合,每个成员可以是普通字符串,也可以是任意 SchemaNode 渲染器节点。列成员上通过columnClassName指定该列容器额外的 CSS 类名,用于控制宽度、边框、背景等视觉样式。
一个典型的双列示例:
[ { "type": "hbox", "className": "b-a bg-dark lter", "columns": [ { "type": "plain", "text": "Col A", "columnClassName": "wrapper-xs b-r" }, "Col B" ] }, { "type": "hbox", "className": "b-a m-t bg-dark lter", "columns": [ { "type": "plain", "text": "w-md", "columnClassName": "w-md wrapper-xs bg-primary b-r" }, "..." ] } ]要点说明:
- 第一组 HBox 展示了两列平均分配的效果:
columns中第一个成员是带columnClassName的plain文本渲染器,第二个成员是纯字符串"Col B"(会被 amis 自动包装渲染); - 第二组 HBox 通过
columnClassName: "w-md wrapper-xs bg-primary b-r"把第一列固定为w-md宽度,其余列自动分配剩余空间,实现"固定列 + 自适应列"的经典布局; className作用于 HBox 外层 DOM,示例中使用了 amis 内置的边框(b-a)、背景(bg-dark、bg-primary)与外边距(m-t)工具类。
混合宽度与等宽分配
HBox 的默认行为是所有列等宽分配(详见下文 Flex 原理)。从 HBox 测试用例 可以看到五种列混排的写法:前三列分别通过columnClassName: 'w-xs'、'w-sm'、'w'固定宽度,后两列不指定宽度自动均分剩余空间。其快照(HBox.test.tsx.snap)表明渲染出的 DOM 结构为:
div.cxd-Hbox.cxd-Hbox--xs ├── div.cxd-Hbox-col.w-xs ├── div.cxd-Hbox-col.w-sm ├── div.cxd-Hbox-col.w ├── div.cxd-Hbox-col ← 未指定宽度,等宽 └── div.cxd-Hbox-col ← 未指定宽度,等宽属性表
下表在原文档属性表基础上,补充了 AMISHBoxSchema 与 AMISHBoxColumn 类型定义中额外声明的属性:
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| type | string | "hbox" | 指定为 HBox 渲染器 |
| className | string | 外层 Dom 的类名 | |
| gap | 'xs' \| 'sm' \| 'base' \| 'none' \| 'md' \| 'lg' | 'xs' | 水平间距 |
| valign | 'top' \| 'middle' \| 'bottom' \| 'between' | 垂直对齐方式 | |
| align | 'left' \| 'right' \| 'between' \| 'center' | 水平对齐方式 | |
| columns | Array | 列集合 | |
| columns[x] | SchemaNode | 成员可以是其他渲染器 | |
| columns[x].columnClassName | string | "wrapper-xs" | 列上类名 |
| columns[x].valign | 'top' \| 'middle' \| 'bottom' \| 'between' | 当前列内容的垂直对齐 | |
| columns[x].width | number \| string | 列宽度,'auto'时自适应内容 | |
| columns[x].height | number \| string | 列高度 | |
| columns[x].style | object | 其他样式,直接写入列 DOM | |
| columns[x].mode | 'normal' \| 'inline' \| 'horizontal' | 列内子表单项默认展示方式 | |
| columns[x].horizontal | FormHorizontal | 水平排版下左右宽度占比细化 | |
| columns[x].visible | boolean | 该列是否显示 | |
| columns[x].visibleOn | AMISExpression | 是否显示表达式(数据驱动) | |
| subFormMode | 'normal' \| 'inline' \| 'horizontal' | 列内子表单项默认展示方式(作用于整个 HBox) | |
| subFormHorizontal | FormHorizontal | 列内水平排版的左右宽度占比(作用于整个 HBox) |
说明一:gap默认值。原文档未标注默认值,但 HBox 的 defaultProps 明确声明gap: 'xs',因此未配置gap时列间默认采用xs档位的水平间距,'none'可彻底取消间距。
说明二:columns[x]链接。原文档中columns[x]指向 SchemaNode,即 amis 的通用节点类型文档,位于 docs/zh-CN/types/schemanode.md。
源码实现:HBox 的渲染流程
HBox 渲染器定义在 packages/amis/src/renderers/HBox.tsx,核心类HBox通过@Renderer({type: 'hbox'})装饰器注册为 amis 渲染器。其渲染流程可以概括为三层:
- 外层容器(render 方法):渲染一个根
<div>,类名由Hbox基础类、className、gap(Hbox--{gap})、valign(Hbox--v{Valign})、align(Hbox--h{Align})组合而成,style原样透传; - 列容器(renderColumn 方法):遍历
columns,为每列生成一个Hbox-coldiv,根据column.width是否为'auto'、是否有自定义宽度,分别附加Hbox-col--auto、Hbox-col--customWidth类名,并把width、height、style合并写入行内样式; - 列内容渲染(renderChild 方法):通过 amis 的
render(region, node, props)递归渲染列内的子节点,并向子节点传递formMode与formHorizontal(优先取列上的column.mode/column.horizontal,其次取subFormMode/subFormHorizontal,最后回落全局formMode/formHorizontal)。
值得注意的细节:renderColumn中先通过isVisible(column, data)判断列的visible/visibleOn,因此可以在 JSON 中按数据条件动态隐藏某一列;列内容被渲染为column/${key}区域,确保每列拥有独立的渲染作用域。
布局原理:基于 Flex 的等宽与间距实现
HBox 的样式定义在 packages/amis-ui/scss/layout/_hbox.scss,全部基于 CSS Flexbox 实现,不依赖栅格系统:
- 外层
.Hbox声明display: flex; flex-direction: row; flex-wrap: nowrap,即单行横向排列、不换行; - 默认列
.Hbox-col声明flex-basis: 0; flex-grow: 1; width: 100%,这正是"所有列等宽分配"的实现基础——每列可伸缩基数相同,剩余空间被均分; - 指定宽度后列会附加
Hbox-col--customWidth,样式置为flex-grow: unset; flex-basis: unset,列宽完全由行内width决定,其余列继续均分剩余空间; - 宽度为
'auto'时列附加Hbox-col--auto,样式为flex: 0 0 auto; width: auto,列宽收缩为内容宽度,且不可伸缩。
gap的实现方式比较特别:外层容器使用负 margin(margin-left/right: calc(var(--gap-*)) * -0.5),列内部使用正 padding(padding-left/right: calc(var(--gap-*)) * 0.5),从而在不改变列宽计算的前提下形成均匀的列间距。xs / sm / base / md / lg各档位对应的间距尺寸由主题变量--gap-*定义(如--gap-xs: var(--sizes-size-3),见 packages/amis-ui/scss/_properties.scss),开发者可通过主题定制统一调整。
对齐方式:水平 align 与垂直 valign
HBox 同时支持水平与垂直两个维度的对齐,由源码中类名映射与样式共同完成:
| 维度 | 属性 | 取值 | 生成的类名 | 样式效果 |
|---|---|---|---|---|
| 水平 | align | left | 无附加类 | 默认左对齐(justify-content不设置) |
| 水平 | align | right | Hbox--hRight | justify-content: flex-end |
| 水平 | align | center | Hbox--hCenter | justify-content: center |
| 水平 | align | between | Hbox--hBetween | justify-content: space-between |
| 垂直 | valign | top | 无附加类 | 默认顶部对齐 |
| 垂直 | valign | middle | Hbox--vMiddle | 列内flex-direction: column; justify-content: center |
| 垂直 | valign | bottom | Hbox--vBottom | 列内flex-direction: column; justify-content: flex-end |
| 垂直 | valign | between | Hbox--vBetween | 列内flex-direction: column; justify-content: space-between |
类名映射逻辑位于 HBox 的 render 方法:垂直对齐通过Hbox--v{ucFirst(valign)}拼装,水平对齐通过Hbox--h{ucFirst(align)}拼装。此外,单列还可以覆盖整体对齐:columns[x].valign会在列容器上生成Hbox-col--v{Valign}类名(见 renderColumn),对应样式.Hbox > .Hbox-col--vTop/vMiddle/vBottom/vBetween只作用于当前列,方便实现"整体居中对齐、某列底部对齐"等混合效果。
列条件渲染与表单模式透传
除原文档属性表列出的字段外,源码还支持两类实用能力:
- 列级条件渲染:
columns[x].visible与columns[x].visibleOn(AMISHBoxColumn 定义)结合isVisible(column, data)判断,可依据页面数据动态显示/隐藏某一列,常用于按角色或业务状态切换列展示; - 表单排版模式透传:当 HBox 列内嵌套表单项时,
mode/horizontal(列级)与subFormMode/subFormHorizontal(HBox 级)会逐层透传给子渲染器(renderColumn 中的传参),使 HBox 可以直接作为表单的水平分段容器使用,无需在每个表单项上重复声明排版方式。
结语
HBox 是 amis 中实现"横向多列布局"的最小成本方案:默认等宽、可选固定/自适应宽度、支持两维对齐与数据驱动显隐,底层只依赖 Flex 的几条规则,性能与可定制性俱佳。其完整类型定义、默认值与渲染逻辑可在 HBox.tsx 中查阅,样式细节可在 _hbox.scss 中验证,测试用例与渲染快照分别在 HBox.test.tsx 与 HBox.test.tsx.snap。若需要更复杂的栅格响应式布局,可参考 amis 的 Grid 等布局组件,HBox 适用于简单、确定的多列场景。
【免费下载链接】amis前端低代码框架,通过 JSON 配置就能生成各种页面。项目地址: https://gitcode.com/GitHub_Trending/am/amis
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考