
Element Plus Collapse 折叠面板完全指南从基础用法到源码级实现原理【免费下载链接】element-plus A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plus导读Collapse折叠面板是 Element Plus 中最常用的内容收纳组件之一用于将大量内容按区块折叠展示帮助页面保持简洁并提升信息检索效率。本文以 Collapse 官方文档 为主体结合 Element Plus 仓库中packages/components/collapse的源码与测试系统讲解 Collapse 的多面板展开、手风琴模式、自定义标题与图标、展开图标位置、防折叠拦截等全部能力并深入剖析其底层状态管理与过渡动画的实现机制。读完本文你将能够熟练使用 Collapse 及其全部 API并理解其在 Vue 3 组合式 API 下的实现原理。一、Collapse 是什么Collapse 用于收纳内容官方文档原话Use Collapse to store contents。它由两部分组成ElCollapse容器组件负责维护当前展开面板的状态列表ElCollapseItem单个面板包含标题区header与内容区content点击标题即可展开/收起。在组件树中二者通过依赖注入provide/inject通信这与 Element Plus 众多父子组件的协作模式一致。源码结构见 collapse 目录核心文件如下文件职责collapse.vue容器组件暴露activeNames与setActiveNamescollapse-item.vue单个面板组件含标题、图标、内容与过渡动画collapse.tsCollapse 的 props 与 emits 类型定义collapse-item.tsCollapseItem 的 props 类型定义use-collapse.ts容器状态管理组合函数use-collapse-item.ts面板交互逻辑组合函数constants.ts父子通信的 InjectionKey 定义collapse.test.tsx组件行为测试用例二、基础用法多面板展开默认情况下Collapse 允许多个面板同时展开。通过v-model绑定当前展开面板的name数组并通过change事件监听变化template div classdemo-collapse el-collapse v-modelactiveNames changehandleChange el-collapse-item titleConsistency name1 divConsistent with real life: in line with the process and logic of real life.../div /el-collapse-item el-collapse-item titleFeedback name2 divOperation feedback: enable the users to clearly perceive their operations.../div /el-collapse-item /el-collapse /div /template script langts setup import { ref } from vue import type { CollapseModelValue } from element-plus const activeNames ref([1]) const handleChange (val: CollapseModelValue) { console.log(val) } /script完整示例见 basic.vue。要点说明v-model 绑定值类型非手风琴模式下是arrayCollapseModelValue对应源码中CollapseModelValue ArrayableCollapseActiveName其中CollapseActiveName string | number见 collapse.ts默认值modelValue默认为空数组[]源码中default: () mutable([])change事件在激活面板发生变化时触发回调参数为当前激活的name数组或字符串。从源码看容器内部将modelValue归一化为数组并存入activeNames这个refuse-collapse.ts同时通过watch深度监听外部modelValue的变化并同步内部状态同文件 L84-L88。也就是说父组件修改绑定值时面板状态会即时响应。多面板展开的切换逻辑在非手风琴模式下点击某个面板的标题时handleChange会先复制当前activeNames若目标name已在其中则移除收起否则追加展开// packages/components/collapse/src/use-collapse.ts const _activeNames [...activeNames.value] const index _activeNames.indexOf(name) if (index -1) { _activeNames.splice(index, 1) } else { _activeNames.push(name) } setActiveNames(_activeNames)随后setActiveNames会同时触发update:modelValue与change两个事件use-collapse.ts保证双向绑定与外部监听都能拿到最新状态。三、手风琴模式Accordion开启accordion属性后同一时间只允许展开一个面板点击已展开的面板则会将其收起template div classdemo-collapse el-collapse v-modelactiveName accordion el-collapse-item titleConsistency name1 div.../div /el-collapse-item el-collapse-item titleFeedback name2 div.../div /el-collapse-item /el-collapse /div /template script langts setup import { ref } from vue const activeName ref(1) /script完整示例见 accordion.vue。手风琴模式有两个关键差异均可在源码中得到印证v-model 绑定值类型变为string或number而不是数组切换逻辑为互斥替换点击某个面板时若它已是当前激活面板则设为空字符串收起否则直接替换为点击的面板// packages/components/collapse/src/use-collapse.ts if (props.accordion) { setActiveNames([activeNames.value[0] name ? : name]) }setActiveNames在向外 emit 时会把手风琴模式下的数组压缩成单个值const value props.accordion ? activeNames.value[0] : activeNames.value emit(UPDATE_MODEL_EVENT, value) emit(CHANGE_EVENT, value)测试用例印证仓库的测试用例 collapse.test.tsx 分别验证了多面板模式与手风琴模式的行为create用例初始激活[1]点击第 3 个面板后1与3同时激活再点击第 1 个面板则1收起——验证多面板同时展开accordion用例初始激活[1]点击第 3 个面板后1自动收起、3激活——验证互斥展开。四、自定义标题Custom title除了title属性你还可以通过具名插槽#title自定义标题内容例如加入图标、徽标或根据展开状态改变样式。重要版本说明自版本^(2.9.10)起title插槽会提供一个isActive属性用于指示当前面板是否处于激活状态这为展开时高亮标题等交互提供了便捷的响应式数据。template div classdemo-collapse el-collapse accordion el-collapse-item name1 template #title{ isActive } div :class[title-wrapper, { is-active: isActive }] Consistency el-icon classheader-icon info-filled / /el-icon /div /template div.../div /el-collapse-item el-collapse-item titleFeedback name2 div.../div /el-collapse-item /el-collapse /div /template script setup langts import { InfoFilled } from element-plus/icons-vue /script style scoped .title-wrapper { display: flex; align-items: center; gap: 4px; } .title-wrapper.is-active { color: var(--el-color-primary); } /style完整示例见 customization.vue。源码中标题插槽的渲染逻辑位于 collapse-item.vuespan :classitemTitleKls slot nametitle :is-activeisActive{{ title }}/slot /span可见title插槽的默认内容为title属性值当提供插槽时插槽内容完全替代默认标题并通过作用域插槽参数isActive拿到激活状态。isActive是一个ComputedRef其计算逻辑为const isActive computed(() collapse?.activeNames.value.includes(unref(name)))即面板的name是否包含在容器的激活列表中。五、自定义图标Custom icon自版本^(2.8.3)起CollapseItem 增加了icon属性与#icon插槽用于自定义面板展开箭头图标。template div classdemo-collapse el-collapse v-modelactiveNames changehandleChange !-- 通过 icon 属性替换为 CaretRight 图标 -- el-collapse-item titleConsistency name1 :iconCaretRight div.../div /el-collapse-item !-- 通过 #icon 插槽完全自定义图标区域 -- el-collapse-item titleFeedback name2 template #icon{ isActive } span classicon-ele {{ isActive ? Expanded : Collapsed }} /span /template div.../div /el-collapse-item /el-collapse /div /template script langts setup import { ref } from vue import { CaretRight } from element-plus/icons-vue import type { CollapseModelValue } from element-plus const activeNames ref([1]) const handleChange (val: CollapseModelValue) { console.log(val) } /script完整示例见 custom-icon.vue。icon 属性的两种用法从源码 collapse-item.ts 可以看到icon的类型为IconPropType支持字符串或 Vue 组件默认值为ArrowRighticon: { type: iconPropType, default: ArrowRight, }传组件如:iconCaretRightElement Plus 内置的图标组件可以直接复用传字符串可以传入一个渲染为图标的字符串名称组件内部通过component :isicon /动态渲染。在模板中#icon插槽同样提供isActive作用域参数见 collapse-item.vueslot nameicon :is-activeisActive el-icon :classarrowKls component :isicon / /el-icon /slot默认情况下图标被包裹在el-icon中并应用el-collapse-item__arrow类激活状态下面板会加上is-active修饰类图标随之旋转旋转样式由主题样式控制。六、自定义展开图标位置Custom icon position自版本^(2.9.10)起Collapse 提供了expand-icon-position属性用于设置展开图标的水平位置可选值为left | right默认righttemplate div classdemo-collapse-position div classflex items-center mb-4 span classmr-4expand icon position: /span el-switch v-modelposition inactive-valueleft active-valueright inactive-textleft active-textright / /div el-collapse :expand-icon-positionposition el-collapse-item titleConsistency name1 div.../div /el-collapse-item /el-collapse /div /template script langts setup import { ref } from vue import type { CollapseIconPositionType } from element-plus const position refCollapseIconPositionType(left) /script完整示例见 custom-icon-position.vue。源码中的实现方式该属性在 collapse.ts 中定义默认值为right在 use-collapse.ts 中它被转换为容器根节点的修饰类export const useCollapseDOM (props: CollapseProps) { const ns useNamespace(collapse) const rootKls computed(() [ ns.b(), ns.b(icon-position-${props.expandIconPosition}), ]) return { rootKls } }也就是说根元素会得到el-collapse与el-collapse-icon-position-left或right两个类图标在左还是在右完全由主题样式通过该类控制而非在组件内部做布局判断——这是一种将布局决策交给 CSS 的轻量实现思路。位置不同还会影响标题文字与图标在行内的排列顺序实现时建议用真实页面预览确认视觉细节。七、防止折叠/展开before-collapse 拦截Prevent collapsing自版本^(2.9.11)起Collapse 支持before-collapse钩子在折叠状态即将变化前调用若返回false或返回的Promise被 reject则阻止本次折叠/展开操作。典型场景是异步保存数据未完成时禁止用户收起面板。template div v-loadingloading classdemo-collapse el-collapse v-modelactiveNames :before-collapsebeforeCollapse el-collapse-item titleConsistency name1 div.../div /el-collapse-item el-collapse-item titleFeedback name2 div.../div /el-collapse-item /el-collapse /div /template script langts setup import { ref } from vue const before ref(true) const activeNames ref([1]) const loading ref(false) const beforeCollapse (): Promiseboolean { loading.value true return new Promise((resolve) { setTimeout(() { loading.value false return resolve(before.value) }, 1000) }) } /script完整示例见 prevent-collapsing.vue。底层调用链解析before-collapse的类型为(name: CollapseActiveName) Awaitableboolean即同步返回boolean或返回Promiseboolean见 collapse.ts。点击面板标题后整个调用链如下use-collapse-item.ts → use-collapse.ts面板的handleHeaderClick被触发若面板disabled则直接 return调用注入的collapse?.handleItemClick(name)handleItemClick中若未设置beforeCollapse则直接执行handleChange否则调用beforeCollapse(name)并校验返回值类型——必须是boolean或Promiseboolean否则抛出ElCollapse: beforeCollapse must return type Promiseboolean or boolean错误见 use-collapse.ts若返回Promise则resolve后仅当结果为true时才执行handleChangereject时仅输出debugWarn警告而不改变状态。if (isPromise(shouldChange)) { shouldChange .then((result) { if (result ! false) handleChange(name) }) .catch((e) { debugWarn(SCOPE, some error occurred: ${e}) }) } else if (shouldChange) { handleChange(name) }这意味着拦截能力同时覆盖展开与收起两个方向并且支持异步决策例如先请求后端再决定。八、完整的 Collapse API 参考以下表格完整继承自 Collapse 官方文档并结合源码补充了类型细节与默认值来源。Collapse Attributes名称说明类型默认值model-value / v-model当前激活面板手风琴模式下为string或number否则为arraystring/array[]accordion是否开启手风琴模式同一时间仅展开一个面板booleanfalseexpand-icon-position ^(2.9.10)设置展开图标位置enumleft \| rightrightbefore-collapse ^(2.9.11)折叠状态变化前的钩子返回false或返回被 reject 的Promise时阻止折叠Function(name) Promiseboolean \| boolean—Collapse Events名称说明类型change激活面板变化时触发参数在手风琴模式下为string否则为array(activeNames: array \| string) voidCollapse Slots名称说明子组件default自定义默认内容Collapse ItemCollapse Exposes通过模板 ref 或组件实例可访问以下方法/属性名称说明类型activeNames当前激活的面板名称ComputedRef(string \| number)[]setActiveNames设置激活面板名称(activeNames: (string \| number)[]) void这两个暴露项定义在 collapse.vue 的defineExpose中可用于命令式地控制面板状态。Collapse Item Attributes名称说明类型默认值name面板的唯一标识string/number—不传时由组件基于注入的 id 自动生成格式为el-collapse-id-{prefix}-{n}title面板标题stringicon ^(2.8.3)面板的展开图标string/ComponentArrowRightdisabled是否禁用该面板booleanfalse关于name的补充源码 use-collapse-item.ts 显示未传name时组件会使用useIdInjection生成唯一 id 作为默认名称。但实践上强烈建议为每个面板显式传name否则 v-model 绑定将难以管理。Collapse Item Slots名称说明类型defaultCollapse Item 的内容—title面板标题内容{ isActive: boolean }icon ^(2.8.3)面板图标内容{ isActive: boolean }Collapse Item Exposes名称说明类型isActive当前面板是否激活ComputedRefboolean \| undefined九、源码级原理父子通信与展开动画1. 状态共享provide / inject容器通过provide(collapseContextKey, { activeNames, handleItemClick })向所有子面板注入上下文use-collapse.tscollapseContextKey是定义在 constants.ts 中的InjectionKey。子面板则通过inject(collapseContextKey)获取该上下文use-collapse-item.ts。这种模式下面板不直接持有状态而是全部委托给容器统一管理这也是多面板互斥、手风琴互斥逻辑能集中实现的前提。2. 交互细节可访问性与表单兼容面板标题渲染为rolebutton的可聚焦元素支持键盘操作通过Space或Enter键也能切换展开状态keydown.space.enter.stophandleEnterClick提供aria-expanded、aria-controls、aria-describedby等无障碍属性内容区使用roleregion标题区域内的input、textarea、select点击不会误触面板切换通过target.closest(input, textarea, select)判断见 use-collapse-item.tsdisabled面板不响应点击且不接收焦点。3. 展开/收起动画ElCollapseTransition面板内容包裹在 collapse-transition 组件内通过操作max-height实现流畅的高度过渡动画。其核心技巧是展开enter时先记录原始 padding将max-height置 0再通过requestAnimationFrame把max-height设置为元素的scrollHeight从而让浏览器为高度变化补间收起leave时先把max-height设为当前scrollHeight确保过渡起点正确再过渡到 0动画结束后统一reset清理内联样式max-height、overflow、padding避免影响后续布局计算。这套动画同时被 Collapse、Drawer 等组件复用是 Element Plus 中高度自适应过渡的标准实现。十、实战建议与注意事项显式声明name每个el-collapse-item都应设置唯一name它是 v-model 与change事件的匹配依据依赖自动生成的 id 会让状态管理不可控。按需选择 v-model 类型非手风琴模式绑定数组手风琴模式绑定string两种模式下change事件的参数类型也随之变化数组 / 字符串。异步拦截结合 loadingbefore-collapse返回Promise时可配合v-loading在等待期间给出视觉反馈官方示例 prevent-collapsing.vue 即采用此模式。图标定制优先级#icon插槽 icon属性 默认ArrowRight。插槽完全覆盖默认图标属性仅替换图标组件而保留el-icon包裹与旋转动画。与 Element Plus 版本对应icon2.8.3、expand-icon-position2.9.10、title插槽isActive参数2.9.10、before-collapse2.9.11均有版本门槛升级组件库时注意查阅 CHANGELOG 确认可用性。命名空间与主题定制组件 DOM 类名统一基于el-collapse命名空间el-collapse-item__header、el-collapse-item__arrow等如需深度定制样式可参考主题样式源文件theme-chalk/src按类名覆盖。结语Collapse 看似简单实则涵盖了 Element Plus 组件设计的多项核心范式父子通过 provide/inject 共享状态、props 驱动 CSS 修饰类、before-collapse异步拦截钩子、基于max-height的高度过渡动画以及完整的键盘与无障碍支持。希望本文从用法到源码的梳理能帮助你在实际项目中更自信地使用 Collapse并从中理解 Element Plus 组件的通用设计思路。【免费下载链接】element-plus A Vue.js 3 UI Library made by Element team项目地址: https://gitcode.com/GitHub_Trending/el/element-plus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考