Radix Vue Collapsible 组件完全指南:受控状态、内容动画与无障碍折叠面板 📅 发布时间:2026/9/17 18:47:25 👁 浏览次数: Radix Vue Collapsible 组件完全指南受控状态、内容动画与无障碍折叠面板【免费下载链接】radix-vueAn open-source UI component library for building high-quality, accessible design systems and web apps for Vue. Previously Radix Vue项目地址: https://gitcode.com/GitHub_Trending/ra/radix-vue导读本文以 radix-vue现名 reka-ui组件库中的 Collapsible折叠面板为对象完整讲解该组件的三个组成部件Root / Trigger / Content的安装方式、API 结构、数据属性与 CSS 变量并结合 CollapsibleRoot.vue、CollapsibleContent.vue 等源码剖析其受控/非受控状态机制、内容尺寸测量动画原理与无障碍实现。读完本文你将掌握如何在 Vue 3 项目中快速接入 Collapsible实现可点击展开/收起的面板、基于 CSS 变量的开合动画以及让隐藏内容可被浏览器搜索的unmount-on-hide用法。Collapsible 是什么Collapsible 是一个交互式组件用于展开/收起一段面板内容常见于「FAQ 问答」、「设置分组折叠」、「详情展开」等场景。它的交互行为遵循 WAI-ARIA 的 Disclosure揭示设计模式即通过一个按钮控件来控制关联区域的可见性。在 radix-vue 组件库中Collapsible 由三个子部件组合而成CollapsibleRoot承载所有部件、管理展开状态的根组件CollapsibleTrigger触发展开/收起的按钮CollapsibleContent被展开/收起的内容容器。从源码结构看Collapsible 被拆分为独立目录 packages/core/src/Collapsible包含CollapsibleRoot.vue、CollapsibleTrigger.vue、CollapsibleContent.vue、index.ts以及配套的 Story 演示story/_Collapsible.vue和完整测试用例Collapsible.test.ts。安装在项目根目录执行以下命令即可将组件库reka-ui 命名空间安装为依赖npm install reka-ui安装完成后即可从reka-ui中按需导入 Collapsible 的各个部件。组件结构AnatomyCollapsible 的最小组合结构如下三个部件缺一不可CollapsibleTrigger必须是CollapsibleRoot的子级而CollapsibleContent通常放在 Trigger 之后script setup import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from reka-ui /script template CollapsibleRoot CollapsibleTrigger / CollapsibleContent / /CollapsibleRoot /templateAPI 参考RootCollapsibleRootCollapsibleRoot包含 Collapsible 的所有部件负责声明展开状态受控或非受控与禁用状态。属性说明类型默认值as组件实际渲染的元素或组件可被asChild覆盖AsTag \| ComponentdivasChild将默认渲染元素替换为传入的子元素并合并其 props 与行为boolean-defaultOpen非受控模式下组件首次渲染时的展开状态booleanfalseopen受控模式下的展开状态可配合v-model双向绑定boolean-disabled为true时阻止用户与 Collapsible 交互boolean-unmountOnHide为true时内容在关闭状态下会被从 DOM 中卸载booleantrue事件Emits事件说明类型update:open展开状态变化时触发[value: boolean]插槽Slots插槽说明类型open当前展开状态作用域插槽boolean数据属性Data Attributes属性值[data-state]open/closed[data-disabled]禁用时存在源码解析从 CollapsibleRoot.vue 可以看到open通过 VueUse 的useVModel实现双向绑定未传open时以defaultOpen作为默认值从而实现「受控」与「非受控」两种模式的无缝切换。onOpenToggle在触发时若处于disabled状态会直接返回防止任何方式点击、键盘、beforematch改变状态。根节点最终渲染为Primitive并输出data-state与data-disabled两个数据属性。TriggerCollapsibleTriggerCollapsibleTrigger是切换 Collapsible 开关状态的按钮。它会自动把点击事件绑定到根节点的onOpenToggle并同步维护aria-controls、aria-expanded等无障碍属性。属性说明类型默认值as组件实际渲染的元素或组件可被asChild覆盖AsTag \| ComponentbuttonasChild将默认渲染元素替换为传入的子元素并合并其 props 与行为boolean-数据属性属性值[data-state]open/closed[data-disabled]禁用时存在源码解析查看 CollapsibleTrigger.vueTrigger 默认渲染为buttonas默认值button且仅当渲染按钮时才自动补上typebutton以避免表单误提交并通过注入的 Root 上下文自动绑定aria-controls指向 Root 注入的内容元素 IDaria-expanded随open状态同步为true/falsedata-state/data-disabled与 Root 保持一致的样式钩子click调用onOpenToggle。由于这些行为全部通过provide/inject上下文自动完成使用者无需手写任何事件与 ARIA 属性。ContentCollapsibleContentCollapsibleContent是包含折叠内容的组件。它基于 Presence 机制管理挂载状态并负责测量内容宽高、输出供动画使用的 CSS 变量。属性说明类型默认值as组件实际渲染的元素或组件可被asChild覆盖AsTag \| ComponentdivasChild将默认渲染元素替换为传入的子元素并合并其 props 与行为boolean-forceMount强制挂载内容便于配合 Vue 动画库做更精细的动画控制boolean-事件Emits事件说明类型contentFound内容被浏览器「查找命中」并自动展开时触发[void]数据属性属性值[data-state]open/closed[data-disabled]禁用时存在CSS 变量变量说明--reka-collapsible-content-width内容打开/关闭时的宽度--reka-collapsible-content-height内容打开/关闭时的高度关于挂载行为默认情况下Content 在关闭时会被卸载unmountOnHide默认为true因此对隐藏内容的动画需要借助 CSS 变量或forceMount来实现。可参考文档中的 PresenceCallout 说明。源码解析Content 的实现是动画能力的关键。在 CollapsibleContent.vue 中组件监听open与 Presence 的present状态变化通过nextTick后调用getBoundingClientRect()实时测量内容的宽高并写入--reka-collapsible-content-width与--reka-collapsible-content-height两个 CSS 变量测量前会临时将transitionDuration置为0s、animationName置为none确保拿到的是「全尺寸」而非动画中间态测量完成后再恢复原始样式首帧挂载时则跳过动画isMountAnimationPrevented内容通过 Presence 包裹hidden属性的取值会根据unmountOnHide动态决定卸载模式下关闭时移除 DOM非卸载模式下则使用hiddenuntil-found。实战示例示例一让内容尺寸带动画Collapsible 官方推荐使用--reka-collapsible-content-width和/或--reka-collapsible-content-height这两个 CSS 变量在内容打开/关闭时制作平滑的尺寸动画。其原理是Content 组件在状态切换时会先测量出内容的完整尺寸并写入 CSS 变量开发者只需在动画关键帧中引用该变量即可无需自己量算高度。// index.vue script setup import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from reka-ui import ./styles.css /script template CollapsibleRoot CollapsibleTrigger…/CollapsibleTrigger CollapsibleContent classCollapsibleContent … /CollapsibleContent /CollapsibleRoot /template/* styles.css */ .CollapsibleContent { overflow: hidden; } .CollapsibleContent[data-stateopen] { animation: slideDown 300ms ease-out; } .CollapsibleContent[data-stateclosed] { animation: slideUp 300ms ease-out; } keyframes slideDown { from { height: 0; } to { height: var(--reka-collapsible-content-height); } } keyframes slideUp { from { height: var(--reka-collapsible-content-height); } to { height: 0; } }要点说明.CollapsibleContent必须设置overflow: hidden否则高度动画期间内容会溢出动画的to/from值直接引用组件注入的--reka-collapsible-content-height打开时从0展开到完整高度关闭时反向收缩你同样可以针对宽度使用--reka-collapsible-content-width实现水平方向的开合效果若需要使用 Vue 过渡组件如Transition做更复杂的动画编排可以在CollapsibleContent上设置forceMount让内容始终驻留 DOM。示例二收起后仍保留内容浏览器可搜索默认情况下unmountOnHide为true折叠隐藏的内容会被直接从 DOM 卸载。若希望内容在收起时仍然保留在页面中以便浏览器如 Chrome 的「在页面中查找」功能能够检索到隐藏文本、并在命中时自动展开可以关闭卸载行为script setup import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from reka-ui /script template CollapsibleRoot :unmount-on-hidefalse … /CollapsibleRoot /template底层原理当unmountOnHide为false时Content 在收起状态下不会被卸载而是使用hiddenuntil-found属性隐藏内容见 CollapsibleContent.vue。这是 HTML 原生支持的「可搜索隐藏」机制——浏览器的页面查找功能可以命中until-found隐藏的内容命中后浏览器会派发beforematch事件Content 组件正是通过useEventListener(currentElement, beforematch, ...)CollapsibleContent.vue监听该事件自动调用onOpenToggle()展开面板并向外触发contentFound事件从而实现「搜索即展开」的完整体验。从测试用例 Collapsible.test.ts 可以看到unmountOnHide: false模式下收起后内容仍保留在 DOM 中断言hidden属性存在且内容文本仍可被找到与文档描述完全一致。无障碍AccessibilityCollapsible 遵循 WAI-ARIA 的 Disclosure 设计模式。组件内部已自动处理以下无障碍细节CollapsibleTrigger自动获得aria-expanded反映当前展开状态与aria-controls指向内容元素 ID内容收起时通过hidden属性或hiddenuntil-found对屏幕阅读器隐藏禁用状态下根节点与 Trigger 均输出data-disabledTrigger 的disabled属性也会同步置位。在源码目录的测试文件 Collapsible.test.ts 中每个场景都运行了axe可访问性扫描expect(await axe(wrapper.element)).toHaveNoViolations()为无障碍行为提供了自动化回归保障。键盘交互按键说明Space空格打开/关闭折叠面板Enter回车打开/关闭折叠面板由于 Trigger 默认渲染为原生buttonSpace 与 Enter 的展开/收起行为由浏览器原生按钮语义提供无需额外 JS 监听若通过asChild将 Trigger 替换为自定义元素请确保该元素自身具备可聚焦与键盘触发能力。小结Collapsible 是 radix-vue 中结构最简洁的交互组件之一却完整体现了组件库的设计哲学三个职责清晰的部件、受控/非受控双模式、as/asChild组合能力、Presence 驱动的挂载管理以及「测量尺寸 → 暴露 CSS 变量 → 开发者自由动画」的渐进增强思路。配合unmount-on-hidefalse与hiddenuntil-found它甚至能原生支持「浏览器查找并自动展开隐藏内容」这一高级场景。无论是实现 FAQ 折叠、设置分组还是任何展开/收起需求你都可以直接基于 docs/content/docs/components/collapsible.md 的 API 与 packages/core/src/Collapsible 的实现快速落地一个无障碍、可动画、行为可靠的折叠面板。【免费下载链接】radix-vueAn open-source UI component library for building high-quality, accessible design systems and web apps for Vue. Previously Radix Vue项目地址: https://gitcode.com/GitHub_Trending/ra/radix-vue创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考