首页
/ Ant Design Splitter(分隔面板)组件完全指南:布局切分、拖拽缩放、可折叠与受控模式实战

Ant Design Splitter(分隔面板)组件完全指南:布局切分、拖拽缩放、可折叠与受控模式实战

2026-09-08 23:05:43作者:贡沫苏Truman

Splitter 是 Ant Design 在布局(Layout)分类下提供的“自由切分指定区域”组件,允许用户通过拖拽分隔条水平或垂直地调整多个面板的大小,并支持最小/最大尺寸约束、面板折叠以及受控模式。本指南以官方中文文档(components/splitter/index.zh-CN.md)为骨架,结合仓库源码与示例,系统讲解其使用场景、全部 API、实现原理与常见实践,帮助你在项目中实现 IDE 式分栏、左右两栏工作台、上下详情区等布局。

何时使用 Splitter

按官方文档,出现以下三类需求时可选用 Splitter:

  • 需要水平或垂直地分隔区域
  • 需要自由拖拽调整各区域大小
  • 需要为指定区域设定最大/最小宽高

它适用于典型的两栏后台工作台、多栏报表编辑区、可收起侧边栏等布局场景。与固定的 Col/Row 栅格不同,Splitter 的面板尺寸可由用户通过分隔条交互式改变。

注意:Splitter 需要通过子元素计算面板大小,因此它的子元素仅支持 Splitter.Panel,其他节点会被过滤。这一约束在入口文件 components/splitter/index.tsx 中通过组合式(Compound)组件挂载 Splitter.Panel,并在 useItems 中仅保留合法元素予以保证。

快速上手:基本用法

Splitter 属于复合组件,使用时需在 Splitter 内部渲染两个及以上 Splitter.Panel

import { Splitter } from 'antd';

const App = () => (
  <Splitter style={{ height: 200, boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)' }}>
    <Splitter.Panel defaultSize="40%" min="20%" max="70%">
      <div>First Panel</div>
    </Splitter.Panel>
    <Splitter.Panel>
      <div>Second Panel</div>
    </Splitter.Panel>
  </Splitter>
);

该示例对应 components/splitter/demo/size.tsx,要点包括:

  • 容器需要显式设置高度(如 height: 200),面板尺寸基于容器实际尺寸计算;
  • defaultSize="40%" 表示初始宽度占比 40%,支持像素与百分比两种类型;
  • min / max 限制面板可被压缩或拉伸的阈值范围,同样支持数字(px)或 '百分比%' 字符串。

尺寸归一化的底层原理

从源码看,所有尺寸最终都会被换算为百分比参与布局。useSizescomponents/splitter/hooks/useSizes.ts)做三件事:

  1. 将传入的 px 值除以容器实际尺寸,得到百分比;字符串以 % 结尾时通过 getPtg 直接解析为 数值/100
  2. 统计已有百分比与剩余空间,将未显式设置尺寸的面板按剩余比例分摊;
  3. 当各面板 min/max 无法同时满足时,走贪心分配逻辑。

核心分配算法位于 components/splitter/hooks/sizeUtil.tsautoPtgSizes:它先计算已定义尺寸总和,若总和恰好为 1 且全部有值则按比例缩放;若有未定义项则先尝试平均分摊,超出边界(min/max 累计)时再贪心为未定义面板逐个填充到允许的最大值。这正是“自由面板自动吃满剩余空间”的机制来源。

在渲染层,Panel.tsx 用 Flex 布局实现:面板设置了 size(受控或来自计算)时使用 flexBasisflexGrow: 0,未设置时 flexGrow: 1 自适应;同时注释说明“SSR 首屏时使用 auto”,规避服务端渲染时无容器尺寸导致的计算错位。

布局方向:horizontal 与 vertical

Splitter 默认水平排列(两个面板左右分布,分隔条可左右拖动)。需要上下分布时传入 vertical

import { Splitter } from 'antd';

const App = () => (
  <Splitter vertical style={{ height: 300 }}>
    <Splitter.Panel>Top</Splitter.Panel>
    <Splitter.Panel>Bottom</Splitter.Panel>
  </Splitter>
);

该示例对应 components/splitter/demo/vertical.tsx

关于方向属性,官方文档需要记住两条规则(自 6.0.0 起):

  • orientation'horizontal' | 'vertical',为推荐写法,默认 'horizontal'
  • vertical:布尔值,默认 false,为旧式便捷写法;
  • layout:与 orientation 语义相同的旧属性,已废弃(deprecated)
  • verticalorientation 同时存在时,orientation 优先

三者最终合并逻辑在 components/splitter/Splitter.tsx 中通过 useOrientation(orientation, vertical, layout) 统一得出方向。开发环境下若仍在使用 layout,控制台会通过 devUseWarning 输出废弃提示(warning.deprecated(!layout, 'layout', 'orientation'))。

面板嵌套与复杂组合

Splitter 支持在面板内部继续嵌套 Splitter,从而构造“左侧一栏 + 右侧上下两栏”这类工作台布局:

import { Splitter } from 'antd';

const App = () => (
  <Splitter style={{ height: 300 }}>
    <Splitter.Panel collapsible>Left</Splitter.Panel>
    <Splitter.Panel>
      <Splitter orientation="vertical">
        <Splitter.Panel>Top</Splitter.Panel>
        <Splitter.Panel>Bottom</Splitter.Panel>
      </Splitter>
    </Splitter.Panel>
  </Splitter>
);

完整示例见 components/splitter/demo/group.tsx。需要提示的是,当 Splitter 嵌套在隐藏的标签页面板等零尺寸容器中时,外层会先跳过尺寸为 0 的容器测量(containerSize === 0 直接返回),避免出现布局异常,相关修复逻辑同样位于 Splitter.tsx 的 onContainerResize 中。仓库内 components/splitter/demo/nested-in-tabs.tsx 即为该场景的示例。

受控模式(sizes 状态同步)

需要把面板尺寸交由 React 状态管理(例如配合“重置”按钮、持久化布局)时,可使用受控写法:把 size 数组的值逐个传给 Splitter.Panel,并通过 onResize 把拖拽后的最新尺寸同步回状态:

import React from 'react';
import { Button, Flex, Splitter, Switch, Typography } from 'antd';

const App = () => {
  const [sizes, setSizes] = React.useState<(number | string)[]>(['50%', '50%']);
  const [enabled, setEnabled] = React.useState(true);

  return (
    <Flex vertical gap="medium">
      <Splitter onResize={setSizes} style={{ height: 200 }}>
        <Splitter.Panel size={sizes[0]} resizable={enabled}>
          <Typography.Text>First</Typography.Text>
        </Splitter.Panel>
        <Splitter.Panel size={sizes[1]}>
          <Typography.Text>Second</Typography.Text>
        </Splitter.Panel>
      </Splitter>
      <Flex gap="medium" justify="space-between">
        <Switch checked={enabled} onChange={() => setEnabled(!enabled)} />
        <Button onClick={() => setSizes(['50%', '50%'])}>Reset</Button>
      </Flex>
    </Flex>
  );
};

完整示例见 components/splitter/demo/control.tsx。受控模式下有几个值得注意的行为:

  • 部分受控会产生开发警告:源码中检测到“部分 Panel 设置了 size、另一部分没有”且未传入 onResize 时,会提示应补上 onResize 或将 size 改为 defaultSize
  • resizable={false} 可以关闭单个面板所在分隔条的拖拽能力(对应 Splitter.Panelresizable,默认 true);
  • 受控尺寸同样支持 px 或百分比字符串,跨端回传的值以百分比形式给出。

此外,Splitter 提供了三个拖拽过程回调,便于精确监听:

  • onResizeStart(sizes):开始拖拽之前触发;
  • onResize(sizes):面板大小变化(拖拽过程中持续触发)时回调;
  • onResizeEnd(sizes):拖拽结束(松手)时回调。

事件接线逻辑集中在 Splitter.tsx 的 onInternalResizeStart / onInternalResizeUpdate / onInternalResizeEnd,它们基于 useResize 对相邻面板做 min/max 联动约束:拖拽第 i 根分隔条时,左/上面板不小于其 min、不大于其 max,同时对称地限制右/下面板,最终“钳制”出合法落点区间。

可折叠(Collapsible)

让面板像侧边栏一样可快速收起/展开,是 Splitter 最常用的增强能力。

面板级折叠

Splitter.Panel 上设置 collapsible(默认 false),分隔条两端会出现折叠箭头(单击一次收起相邻面板、再次单击展开):

<Splitter style={{ height: 200 }}>
  <Splitter.Panel collapsible min="20%">
    First
  </Splitter.Panel>
  <Splitter.Panel collapsible>
    Second
  </Splitter.Panel>
</Splitter>

collapsible 支持三种形态(见 interface.tsPanelProps 定义):

  • booleantrue 表示面板左右/上下两个方向都可折叠;
  • { start?: boolean; end?: boolean }:精细化控制“朝向相邻前一个面板”与“朝向相邻后一个面板”两个方向是否可折叠;
  • 对象形式下可额外指定 showCollapsibleIcon?: boolean | 'auto' 控制折叠图标的显示时机(见下文)。

代码层面对布尔值与对象做了归一化:useItemsgetCollapsible 会把 true 展开为 { start: true, end: true, showCollapsibleIcon: 'auto' },并保证每个面板都具备完整的折叠描述对象。示例可参考 components/splitter/demo/collapsible.tsx 与多面板场景 components/splitter/demo/multiple.tsx(后者演示了 collapsible={{ start: true }} 仅允许朝一侧收起)。

Splitter 级折叠配置(6.4.0+)

自 6.4.0 起,折叠相关配置集中到了 Splittercollapsible 属性:

子项 说明 类型
motion 是否开启折叠动画(开启后动画时长跟随 Component Token 配置) boolean
icon.start / icon.end 自定义折叠图标 ReactNode

同时 collapsibleIcon(6.0.0 引入)已废弃,迁移到 collapsible.icon;源码会给出对应废弃警告 warning.deprecated(!collapsibleIcon, 'collapsibleIcon', 'collapsible.icon')

折叠动画实现细节:Splitter 仅在 collapsible?.motion 开启且当前没有正在拖拽(movingIndex === undefined)时,为面板追加 panel-transition 样式类,使宽度/高度变化产生过渡;面板内部在折叠态(size === 0)会追加 panel-hidden 类。相关判断见 Splitter.tsxPanel.tsx

折叠方向与分隔条方位相关:在横向布局(horizontal)中,start 折叠的是左侧面板,对应默认图标为 LeftOutlinedend 对应 RightOutlined;垂直布局则上下对应 UpOutlined / DownOutlined,图标选取逻辑在 SplitBar.tsx 中完成,自定义 icon 存在时优先渲染自定义节点。

折叠图标显示策略(showCollapsibleIcon)

Panel.collapsible 对象中的 showCollapsibleIcon 提供 boolean | 'auto' 三种取值(默认 'auto',自 5.27.0 起支持),可见示例 components/splitter/demo/collapsibleIcon.tsx

  • true:折叠按钮始终可见(对应样式类 collapse-bar-always-visible);
  • false始终隐藏,但保留键盘操作能力(折叠仍可通过按键触发);
  • 'auto'仅 hover 到分隔条时显示(对应 collapse-bar-hover-only),是默认体验。

多个相邻面板都声明折叠时,两端按钮的可见模式由 useResizable 汇总取并集(只要有任一方要求显示则显示,都未指定时退回 'auto')。折叠/展开回调为 Splitter.onCollapse(collapsed, sizes)(5.28.0+),其中 collapsed: boolean[] 表示各面板当前是否处于收起(尺寸为 0)状态;触发一次折叠后组件同时会派发一次 onResizeonResizeEnd,保证外部受控状态一致。

destroyOnHidden:折叠时销毁面板内容(6.4.0+)

默认折叠面板仅把尺寸置 0,面板 DOM 仍挂载、内容不卸载(便于快速恢复)。若希望折叠时真正卸载内容以节省渲染开销,可设置 destroyOnHidden

  • Splitter 上设置:作用于所有面板(默认 false);
  • 在单个 Splitter.Panel 上设置:覆盖 Splitter 级别的配置(item.destroyOnHidden ?? destroyOnHidden 取面板优先)。

对应实现位于 Panel.tsx:destroyOnHidden && isCollapsed 时不再渲染 children

延迟渲染模式(lazy,5.23.0+)

对大面板、复杂内容或性能敏感场景,可给 Splitterlazy 开启“预览式”拖拽:

<Splitter lazy style={{ height: 200 }}>
  <Splitter.Panel defaultSize="40%" min="20%" max="70%">
    First
  </Splitter.Panel>
  <Splitter.Panel>
    Second
  </Splitter.Panel>
</Splitter>

示例见 components/splitter/demo/lazy.tsx。开启 lazy 后,拖拽过程中不会实时重排面板,而是在拖拽位置渲染一条半透明“预览条”;鼠标/触摸释放时才一次性提交新尺寸(onOffsetUpdate(..., lazyEnd = true) 触发),从而把昂贵的内容重排从高频 move 事件中剥离出来。

实现要点位于 SplitBar.tsx

  • move 阶段调用 handleLazyMove,先通过 getConstrainedOffset 把原始位移按 ariaMin/ariaMax 约束到合法范围,再写入 CSS 变量 --bar-preview-offsettransformStyle[varName('bar-preview-offset')])驱动预览条位移;
  • up/end 阶段调用 handleLazyEnd,一次性提交约束后的偏移量,并将预览条复位;
  • 预览条仅在使用 window 级监听(mousemove/touchmove)拖拽时出现,其样式类为 -bar-preview(激活态追加 -bar-preview-active)。

双击分隔条重置尺寸(onDraggerDoubleClick,6.3.0+)

自 6.3.0 起,Splitter 提供 onDraggerDoubleClick(index: number) 回调,配合受控状态即可实现“双击分隔条恢复初始尺寸”:

const defaultSizes = ['30%', '40%', '30%'];

const App = () => {
  const [sizes, setSizes] = React.useState<(number | string)[]>(defaultSizes);

  return (
    <Splitter
      style={{ height: 200 }}
      onResize={setSizes}
      onDraggerDoubleClick={() => setSizes(defaultSizes)}
    >
      <Splitter.Panel size={sizes[0]}>Panel 1</Splitter.Panel>
      <Splitter.Panel size={sizes[1]}>Panel 2</Splitter.Panel>
      <Splitter.Panel size={sizes[2]}>Panel 3</Splitter.Panel>
    </Splitter>
  );
};

完整示例见 components/splitter/demo/reset.tsx。实现上,SplitBar 使用原生 onDoubleClick 派发该事件;为防止双击同时误触发拖拽起点,内部用 DOUBLE_CLICK_TIME_GAP = 300 毫秒过滤:两次 mousedown 间隔小于 300ms 时直接忽略(Prevent drag start if it's a double-click action)。

API 总览

通用属性(className、style、前缀类名等)参考 docs/react/common-props 中通用属性约定。以下为官方文档记录的组件完整 API。

Splitter

参数 说明 类型 默认值 版本 全局配置
classNames 用于自定义组件内部各语义化结构的 class,支持对象或函数 Record<SemanticDOM, string> | (info: { props }) => Record<SemanticDOM, string> - 6.0.0 6.0.0
collapsible motion 是否开启折叠动画,icon 自定义折叠图标 { motion?: boolean; icon?: { start?: ReactNode; end?: ReactNode } } - 6.4.0 ×
collapsibleIcon 折叠图标(已废弃,请使用 collapsible.icon { start?: ReactNode; end?: ReactNode } - 6.0.0 ×
destroyOnHidden 折叠时(size 为 0)销毁面板内容,应用于所有面板,可在单个面板上覆盖 boolean false 6.4.0 ×
draggerIcon 拖拽图标 ReactNode - 6.0.0 ×
layout 布局方向(已废弃,请使用 orientation 'horizontal' | 'vertical' 'horizontal' - ×
lazy 延迟渲染模式 boolean false 5.23.0 ×
onCollapse 展开-收起时回调 (collapsed: boolean[], sizes: number[]) => void - 5.28.0 ×
orientation 布局方向 'horizontal' | 'vertical' 'horizontal' 6.0.0 ×
styles 用于自定义组件内部各语义化结构的行内 style,支持对象或函数 Record<SemanticDOM, CSSProperties> | (info: { props }) => Record<SemanticDOM, CSSProperties> - 6.0.0 6.0.0
vertical 排列方向,与 orientation 同时存在时以 orientation 优先 boolean false 6.0.0 ×
onDraggerDoubleClick 双击拖拽条回调 (index: number) => void - 6.3.0 ×
onResize 面板大小变化回调 (sizes: number[]) => void - - ×
onResizeEnd 拖拽结束回调 (sizes: number[]) => void - - ×
onResizeStart 开始拖拽之前回调 (sizes: number[]) => void - - ×

全局配置列打“√/6.0.0”的项(classNames / styles)支持通过 ConfigProvidercomponentConfig.splitter 做主题级全局定制(类型声明见 config-provider 使用链与 interface.ts 的语义类型定义)。拖拽图标定制示例可查看 components/splitter/demo/customize.tsxdraggerIcon)与 dragger 语义样式。

Splitter.Panel

参数 说明 类型 默认值 版本
collapsible 快速折叠 boolean | { start?: boolean; end?: boolean; showCollapsibleIcon?: boolean | 'auto' } false showCollapsibleIcon: 5.27.0
defaultSize 初始面板大小,支持数字 px 或文字 '百分比%' 类型 number | string - -
destroyOnHidden 折叠时(size 为 0)销毁面板内容,覆盖 Splitter 的 destroyOnHidden boolean - 6.4.0
max 最大阈值,支持数字 px 或文字 '百分比%' 类型 number | string - -
min 最小阈值,支持数字 px 或文字 '百分比%' 类型 number | string - -
resizable 是否开启拖拽伸缩 boolean true -
size 受控面板大小,支持数字 px 或文字 '百分比%' 类型 number | string - -

关于 min / max / size / defaultSize 的单位换算,再次强调一个易错点:数字一律按 px、带 % 的字符串按百分比解释,且同一根分隔条两侧的约束会共同作用——实际可拖拽区间是两侧 min/max 交集钳制后的结果(见 Splitter.tsxariaMinStart/ariaMinEndariaMaxStart/ariaMaxEnd 的取最大/最小运算)。

Semantic DOM:语义化结构与样式定制(6.0.0+)

Splitter 的 classNames / styles 支持对组件内部语义化节点进行精准定制,且支持对象或函数两种形态(函数接收 { props },可在合并后的 props 基础上动态返回)。可用语义节点如下:

节点 说明
root 根元素,承载 flex 布局、宽高、对齐与拉伸样式
panel 面板元素,承载 flex basis、增长比例与面板容器样式
dragger 拖拽控制元素,可细分 { default, active } 两个状态

其中 dragger 比较特殊:直接写字符串等价于给 default 态,完整写法为:

<Splitter
  classNames={{
    root: 'my-splitter',
    panel: 'my-panel',
    dragger: { default: 'my-dragger', active: 'my-dragger-active' },
  }}
  styles={{
    dragger: {
      default: { background: '#eee' },
      active: { background: '#1677ff' },
    },
  }}
>
  ...
</Splitter>

源码通过 useMergeSemanticuseSemanticRootStyle 实现上下文、props 与 rootClassName 的逐层合并,并把 dragger: 'a' 自动归一化为 { default: 'a' }(见 Splitter.tsx 中的配置项 _default: 'default')。交互细节上,active 态在拖拽进行中(movingIndex === index)生效,而 default 态是常态样式。官方语义化示例见 components/splitter/demo/_semantic.tsxcomponents/splitter/demo/style-class.tsx

主题变量(Design Token)

Splitter 暴露一组组件级 Design Token,可通过主题算法或 ConfigProvider 定制分隔条的视觉尺寸。从 components/splitter/style/index.ts 的类型定义可看到以下 Token:

Token 说明 备注
splitBarDraggableSize 拖拽标识元素大小 推荐使用
splitBarSize 拖拽元素显示大小 -
splitTriggerSize 拖拽触发区域大小 决定可命中拖拽的热区宽度
resizeSpinnerSize 拖拽标识元素大小 已废弃,请使用 splitBarDraggableSize

这些 Token 被注册进主题组件接口(见 components/theme/interface/components.tsSplitter?: SplitterComponentToken),因此会出现在组件的 <ComponentTokenTable component='Splitter' /> 表格中,供主题面板实时查看与覆盖。需要说明的是,样式函数内部对未定义 Token 有代码级兜底(splitTriggerSize || 6splitBarDraggableSize ?? resizeSpinnerSize || 20),具体全局默认值仍以主题实际派发为准。

无障碍与键盘操作

Splitter 的分隔条以语义化元素渲染,内置基础无障碍支持(见 SplitBar.tsx):

  • 拖拽条使用 role="separator",并携带 aria-valuenow / aria-valuemin / aria-valuemax(以百分比形式表示当前分隔位置与可拖动区间);
  • 方向为横向时 aria-orientation="vertical"(分隔条把空间切成左右两份,语义上与原生 vertical separator 一致),垂直布局相反;
  • resizable={false} 时追加 aria-disabled
  • 折叠按钮使用 role="button" + tabIndex={0},支持 Enter 与空格键触发折叠(onCollapseKeyDown),并带有 aria-label
  • 拖拽期间容器会渲染一块透明的 -mask 遮罩,防止光标移出组件导致的状态丢失。

源码结构速览与扩展指引

若要在源码层面继续深入,可参考以下文件:

结语

Splitter 是一个把“尺寸计算、拖拽手势、折叠动效、无障碍与主题定制”完整封装在一个复合组件中的布局利器。使用时只要记住四条主线即可快速上手:方向用 orientation(避免 layout尺寸约束用 min/max + defaultSize受控同步用 size + onResize折叠能力用 Panel.collapsibleSplitter.collapsible.motion;性能敏感场景再叠加 lazydestroyOnHidden。结合本文对照仓库内示例与源码,即可在项目中稳定落地各种可拖拽分栏布局。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.16 K
2.78 K
kernelkernel
deepin linux kernel
C
34
18
docsdocs
暂无描述
Markdown
904
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
934
1.86 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
862
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.96 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.38 K
1.47 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
535
606
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
549
398
leetcodeleetcode
🔥LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer(第 2 版)》、《程序员面试金典(第 6 版)》题解
Markdown
77
23