Arco Design 折叠面板 Collapse 组件完全指南:API 详解、源码原理与实战用例

原创2026-10-12 11:28:29452 阅读
文章标签:UI组件前端设计系统

Arco Design 折叠面板 Collapse 组件完全指南:API 详解、源码原理与实战用例

折叠面板(Collapse)是 Arco Design React 组件库中用于「分组与隐藏复杂内容区域」的核心数据展示组件,支持多面板展开、手风琴模式、无边框简洁样式、懒加载与隐藏销毁等多种交互形态。本文基于仓库内 components/Collapse/README.zh-CN.md 官方文档,结合 collapse.tsx、item.tsx 源码实现与完整测试用例,系统讲解 Collapse 的全部 API、底层工作原理与可复制的实战代码,读完即可在业务中直接落地使用。

一、组件概览:Collapse 能解决什么问题

Collapse 用于把复杂的内容区域分组收纳:默认可以同时展开多个面板,也可以只展开某几个面板;折叠时内容区通过高度过渡动画平滑收起。在 Arco Design 中,它由两个部分组成:

  • Collapse:容器组件,统一管理展开状态(activeKey)、手风琴模式、图标、触发区域等配置;
  • Collapse.Item:单个面板,通过必填的 name 与容器交互。

从 components/index.tsx 可以看到组件以 Collapse 命名导出,并且 Collapse.Item 是挂载在容器上的静态子组件(见 collapse.tsx 中 CollapseComponent.Item = CollapseItem),使用时无需单独引入。

典型使用形态如下:

import { Collapse } from '@arco-design/web-react';

const CollapseItem = Collapse.Item;

<Collapse defaultActiveKey={['1', '2']}>
  <CollapseItem header="面板标题一" name="1">
    面板内容一
  </CollapseItem>
  <CollapseItem header="面板标题二" name="2">
    面板内容二
  </CollapseItem>
</Collapse>

二、Collapse API 全量解析

Collapse 的全部参数定义在 components/Collapse/interface.ts,默认值在 collapse.tsx 中集中声明(bordered: true、lazyload: true、expandIconPosition: 'left')。

参数名 描述 类型 默认值 版本
accordion 是否是手风琴模式 boolean - -
bordered 无边框样式 boolean true -
destroyOnHide 是否销毁被折叠的面板 boolean - -
lazyload 设置为 true 时,挂载时不会渲染被隐藏的面板 boolean true -
expandIconPosition 展开图标的位置 'left' | 'right' left -
triggerRegion 可触发折叠操作的区域 'header' | 'icon' - 2.41.0
expandIcon 自定义展开图标 ReactNode - -
activeKey 当前面板选中值 string | string[] - -
className 节点类名 string | string[] - -
defaultActiveKey 默认展开的面板 string | string[] - -
style 节点样式 CSSProperties - -
onChange 展开面板改变时触发 (key: string, keys: string[], e) => void - -

其中几个关键参数的底层行为值得深入理解:

受控与非受控切换。 源码使用 useMergeValue 管理 activeKeys,onItemClick 中先复制当前 keys、移除或追加目标 key,随后通过 if (!('activeKey' in props)) 判断是否受控:只有未传入 activeKey 时才更新内部状态,最后无论受控与否都会回调 onChange(key, newActiveKeys, e)(见 collapse.tsx)。因此:

  • 只传 defaultActiveKey:非受控,用户点击面板由组件内部维护展开状态;
  • 传 activeKey 但不传 onChange:受控但状态不更新,表现为点击无效,适合纯展示场景;
  • 传 activeKey 且传 onChange:完全受控,需在回调中自行更新 activeKey。

手风琴模式。 accordion 为 true 时,内部通过 getActiveKeys 对 keys 做 slice(0, 1) 截断,展开新面板时直接替换为 <a href="https://link.gitcode.com/i/1b897d5a4beb7b2cfde3ad2bbf7f4207" target="_blank">key](见 [collapse.tsx),保证任意时刻最多只有一个面板处于展开状态。

默认展开图标。 未自定义 expandIcon 时,图标位置为 left 使用 IconCaretRight,为 right 时自动切换为 IconCaretLeft(见 collapse.tsx),省去了手动适配箭头方向的麻烦。

三、Collapse.Item API 全量解析

单个面板的参数定义在 components/Collapse/interface.ts,面板渲染逻辑在 item.tsx。

参数名 描述 类型 默认值 版本
destroyOnHide 面板被折叠时是否销毁节点,优先级高于 Collapse 的 destroyOnHide boolean - -
disabled 是否禁用 boolean - -
showExpandIcon 是否展示展开按钮 boolean true -
name 对应 activeKey,当前面板组件的唯一标识 string (必填) - -
expandIcon 自定义展开图标 ReactNode - -
extra 额外节点 ReactNode - -
header 折叠面板头部内容,允许自定义 ReactNode - -
className 节点类名 string | string[] - -
contentStyle 内容区域的附加样式 CSSProperties - 2.15.0
style 节点样式 CSSProperties - -

name 是唯一标识且必填,它决定该面板与 activeKey / defaultActiveKey 的对应关系;面板是否展开由 ctx.activeKeys?.indexOf(name) > -1 判定(见 item.tsx)。

图标优先级。 expandIcon 的解析逻辑是:面板自身显式传入 expandIcon 时使用自身图标,否则回退到容器的 expandIcon;showExpandIcon={false} 时不渲染图标,同时容器会给面板追加 -no-icon 类名,自动收起图标占位的内边距(见 item.tsx 与 index.less)。

destroyOnHide 优先级。 源码通过 'destroyOnHide' in props 判断面板是否显式声明了该属性,声明则以面板自身为准,否则继承容器配置(见 item.tsx)。

extra 不参与折叠交互。 extra 渲染在头部最右侧,其点击事件通过 e.stopPropagation() 阻断冒泡(见 item.tsx),因此放置在 extra 中的操作按钮(如更多菜单、设置图标)不会误触发展开/折叠。

四、核心实战用例

仓库 components/Collapse/demo 目录提供了 10 个官方示例,覆盖了绝大多数业务形态,下面逐一给出可直接复制的代码。

4.1 基本用法:默认展开多个面板

不传任何状态参数时默认全部折叠;通过 defaultActiveKey 可指定初始展开的面板。header 支持任意 ReactNode,disabled 面板不可点击但仍可正常展示内容:

import { Collapse, Divider } from '@arco-design/web-react';
const CollapseItem = Collapse.Item;

<Collapse defaultActiveKey={['1', '2']} style={{ maxWidth: 1180 }}>
  <CollapseItem header="面板标题一" name="1">
    面板内容一
    <Divider style={{ margin: '8px 0' }} />
    面板内容二
  </CollapseItem>
  <CollapseItem header="禁用面板示例" name="2" disabled>
    该面板无法被展开或折叠。
  </CollapseItem>
</Collapse>

4.2 手风琴模式

设置 accordion 后,同一时刻仅允许展开一个面板,展开新面板会自动收起旧面板:

import { Collapse } from '@arco-design/web-react';
const CollapseItem = Collapse.Item;

<Collapse accordion style={{ maxWidth: 1180 }}>
  <CollapseItem header="北京字节跳动科技有限公司" name="1">内容一</CollapseItem>
  <CollapseItem header="公司介绍" name="2">内容二</CollapseItem>
  <CollapseItem header="底层 AI 技术" name="3">内容三</CollapseItem>
</Collapse>

测试用例 expand and collapse correctly when accordion 验证了手风琴模式下连续点击三个面板后 activeKey 始终只有 1 个、且为最后点击的面板(见 components/Collapse/test/index.test.tsx)。

4.3 嵌套面板

Collapse 支持在面板内容中嵌套新的 Collapse,注意子级面板的 name 使用 '1.1'、'1.2' 这类点号命名即可与外层 '1' 天然隔离:

<Collapse defaultActiveKey={['1']}>
  <CollapseItem header="外层面板" name="1">
    <Collapse defaultActiveKey={'1.1'}>
      <CollapseItem header="内层面板一" name="1.1">内层内容</CollapseItem>
      <CollapseItem header="内层面板二" name="1.2">内层内容</CollapseItem>
    </Collapse>
  </CollapseItem>
</Collapse>

4.4 简洁无边框面板

传入 bordered={false} 去掉容器外边框,得到更轻盈的视觉样式;源码中容器类名会在 -border 与 -borderless 之间切换(见 collapse.tsx):

<Collapse bordered={false} defaultActiveKey={['1']} style={{ maxWidth: 1180 }}>
  {/* ...CollapseItem 同上 */}
</Collapse>

4.5 自定义面板样式

通过 CollapseItem 的 style 可实现卡片化、留白化的自定义外观,配合 bordered={false} 效果更佳:

const customStyle = {
  borderRadius: 2,
  marginBottom: 24,
  border: 'none',
  overflow: 'hidden',
};

<Collapse bordered={false} defaultActiveKey={['1', '2']}>
  <CollapseItem style={customStyle} header="自定义样式面板一" name="1">内容</CollapseItem>
  <CollapseItem style={customStyle} header="自定义样式面板二" name="2">内容</CollapseItem>
</Collapse>

4.6 头部额外节点

extra 常配合图标用于在头部右侧放置操作入口(如 IconMoreVertical);因为 extra 的点击被阻断冒泡,点击它不会触发面板折叠:

import { IconMoreVertical } from '@arco-design/web-react/icon';

<Collapse defaultActiveKey={['1']}>
  <CollapseItem
    header="带操作菜单的面板"
    name="1"
    extra={<IconMoreVertical />}
  >
    面板内容
  </CollapseItem>
</Collapse>

4.7 展开图标位置与隐藏

expandIconPosition 支持 left / right 两个位置;若需要彻底隐藏展开图标,可传 expandIcon={null}。以下示例用 Radio.Group 动态切换三种形态:

import React from 'react';
import { Collapse, Radio, Grid, Typography } from '@arco-design/web-react';

const [position, setPosition] = React.useState('left');

<Collapse
  {...(position
    ? { expandIconPosition: position }
    : { expandIcon: null })}
>
  <CollapseItem header="面板一" name="1" extra={<IconInfoCircle />}>内容</CollapseItem>
</Collapse>

4.8 Lazyload:首次展开时才渲染内容

lazyload 默认值为 true。开启后,被隐藏面板的 children 在挂载阶段不会渲染,直到该面板第一次展开才渲染并保留。测试用例 render lazyload correctly 验证了:初始只有 defaultActiveKey=<a href="https://link.gitcode.com/i/984e6cd5994eb23fc335fef2c176b5c7" target="_blank">'0'] 对应的 1 个 .arco-collapse-item-content,点击第二个面板后变为 2 个(见 [components/Collapse/test/index.test.tsx)。这一机制对「面板内含重组件 / 大数据列表 / 图表」的场景非常有用,可显著降低首屏渲染成本:

<Collapse lazyload defaultActiveKey={['1']}>
  <CollapseItem header="首次展开才渲染" name="1">内容</CollapseItem>
</Collapse>

4.9 DestroyOnHide:折叠时销毁节点

与 lazyload 不同,destroyOnHide 在面板折叠时卸载内容节点,重新展开时重新挂载。适合不希望保留面板内部状态(如表单输入、播放器进度)的场景:

<Collapse destroyOnHide defaultActiveKey={['1', '2']}>
  <CollapseItem header="折叠即销毁的面板" name="1">内容</CollapseItem>
</Collapse>

两者的底层差异体现在 item.tsx 的 Transition 配置上:

  • mountOnEnter:destroyOnHide || lazyload 决定——lazyload 只在首次进入时挂载,destroyOnHide 则每次折叠都会卸载;
  • unmountOnExit:destroyOnHide 决定折叠动画结束后是否真正卸载 DOM。

4.10 triggerRegion:精确控制触发区域(2.41.0 起)

triggerRegion 用于限制「哪些区域点击会触发展开/折叠」,取值 'header'(仅标题文字区域)或 'icon'(仅展开图标区域)。事件处理函数按区域分级:icon 为 0 级、标题为 1 级、头部容器为 2 级,命中规则见 item.tsx——特别地,当 triggerRegion="header" 时,点击图标同样会触发切换,避免用户困惑:

<Collapse defaultActiveKey={['1']} triggerRegion="header">
  <CollapseItem header="仅标题可触发折叠" name="1">内容</CollapseItem>
</Collapse>

测试用例 expand and collapse correctly with trigger region 精确验证了该交互:点击 header 空白处不触发、点击标题文字触发展开、再次点击图标区域收起(见 components/Collapse/test/index.test.tsx)。

五、源码原理深度解析

5.1 状态管理:Context 驱动 + 受控合并

容器通过 CollapseContext.Provider 把 activeKeys、onToggle、expandIcon、expandIconPosition、triggerRegion、lazyload、destroyOnHide 下发给所有面板(见 collapse.tsx),面板通过 useContext(CollapseContext) 消费。整个组件不依赖额外状态管理库,props 经 useMergeProps 与 ConfigProvider 的 componentConfig.Collapse 合并,也就是说可以通过 ConfigProvider 为全局所有 Collapse 统一设置默认参数(对应测试 components/Collapse/test/index.test.tsx 中的 componentConfigTest)。

5.2 展开/折叠动画:基于 react-transition-group 的高度过渡

内容区的展开收起动画不是简单的显隐切换,而是精确的高度过渡(见 item.tsx):

  1. onEnter:设置 height: 0、display: block;
  2. onEntering:读取 scrollHeight 并设置为显式像素高度,触发过渡动画;
  3. onEntered:还原为 height: auto,避免内容动态变化时高度受限;
  4. 收起时 onExit 先记录 offsetHeight 并强制 reflow,再过渡到 height: 0,最后 onExited 设置 display: none。

对应样式上,内容区 transition: height 0.2s、图标 transition: transform 0.2s,展开时图标 rotate(90deg) 旋转(见 components/Collapse/style/index.less)。

5.3 键盘可访问性与无障碍

头部区域是 role="button",带 aria-disabled、aria-expanded、tabIndex,并注册了 useKeyboardEvent 的 onPressEnter 监听,支持按回车键切换面板展开状态(见 item.tsx),这是 2.39.0 版本引入的能力(见 components/Collapse/changelog/index.zh-CN.md)。内容区使用 role="region"。此外容器还支持 rtl 模式,开启后自动添加 -rtl 类名并设置 direction: rtl。

5.4 样式 Token 与主题定制

Collapse 的样式全部由 components/Collapse/style/token.less 中的 Design Token 驱动,主要包括:

  • 边框:@collapse-border-width、@collapse-color-border(color-neutral-3)、@collapse-border-radius;
  • 标题:@collapse-title-font-size、@collapse-title-color-text、@collapse-title-color-bg、展开态标题加粗 @collapse-title-font-weight_active(500);
  • 内容:@collapse-content-color-bg(color-fill-1)、@collapse-content-font-size;
  • 图标:@collapse-expand-icon-size(14px)、@collapse-color-expand-icon(color-neutral-7);
  • 间距:@collapse-title-padding-horizontal(13px)、@collapse-content-padding-vertical。

这些 token 都基于 var(--arco-*) CSS 变量实现,因此跟随 Arco Design 的全局主题(浅色/深色、品牌色)自动联动,也可通过 ConfigProvider 统一覆盖。

六、事件与受控行为细节

  • onChange 签名:(key: string, keys: string[], e) => void,key 是本次发生变化的那个面板的 name,keys 是切换后的完整展开 key 数组,e 是原生事件对象。注意源码在受控场景下 keys 中已经包含本次切换的结果,直接 setState 即可。
  • onChange 只触发一次:历史上存在「点击 icon 或 title 触发 2 次 onChange」的 bug,已在 2.44.1 修复,并有回归测试 onChange should be called only once on expanded or collapsed 兜底(见 components/Collapse/test/index.test.tsx)。
  • 事件冒泡:2.51.0 修复了父节点 onClick 不冒泡的问题,测试 onClick should propagate to parent nodes 验证了点击面板头部后父容器监听器可正常收到事件。

七、版本演进与选型建议

从 components/Collapse/changelog/index.zh-CN.md 可以看到 Collapse 的演进脉络:

  • 2.15.0:新增 Collapse.Item 的 contentStyle,快速定制内容区样式;
  • 2.39.0:支持键盘(回车)切换面板;
  • 2.41.0:新增 triggerRegion,精确控制折叠触发区域;
  • 2.44.1:修复重复触发 onChange 的缺陷;
  • 2.51.0:修复父级节点 onClick 冒泡;
  • 2.66.x:修复 React 19 兼容性与异步内容展开不完整问题。

据此可以给出选型建议:若面板内容较重(图表、长列表),优先依赖默认开启的 lazyload;若内容包含表单等有状态组件且希望每次展开都是全新状态,使用 destroyOnHide;需要「一次只开一个」的导航式场景使用 accordion;交互上对点击区域有严格约束时使用 triggerRegion。

八、常见问题速查

  • 面板点击不展开:检查是否传入了 activeKey 但未在 onChange 中更新状态(受控模式未闭环);检查该面板 name 是否与 activeKey 一致;检查是否误设 disabled。
  • 默认所有面板都展开:lazyload 默认为 true 只影响渲染时机,不影响展开状态;确认 defaultActiveKey 是否拼写正确。
  • extra 点击误触发折叠:仓库已通过 stopPropagation 处理,若使用自定义渲染结构,请勿在 extra 内再嵌套可点击的 header 结构。
  • 折叠动画异常:内容区高度依赖 scrollHeight 计算,若内容在动画期间异步加载导致高度变化,参考 2.66.1 的修复——确保在展开完成后再注入异步内容。
登录后查看全文
arco-design