用 ref 控制折叠面板高度动画的关键是手动获取 scrollheight + 强制重绘 + 过渡 height;因 css transition 对 height: auto 无效,需先设 auto 获取真实高度,再通过 offsetheight 触发重绘,最后设置目标像素值。

用 ref 控制折叠面板子项的高度展开动画,核心在于手动读取真实内容高度 + 强制重绘 + 过渡 height。Vue 的 ref 提供了对 DOM 元素的直接访问能力,这是实现精准高度动画的关键。
关键原理
CSS 的 transition: height 对 auto 值无效,所以不能直接设 height: auto → height: 0。必须:
- 先让元素“可见但不可见”(如
height: auto),获取其scrollHeight; - 然后强制浏览器重绘(如读取
offsetHeight); - 再设置目标高度(
0px或具体像素值),触发过渡。
ref 就是用来拿到这个真实 DOM 节点的。
步骤与写法(Vue 3 Composition API)
<template><div class="collapse-panel">
<button>展开/收起</button>
<div ref="panelRef" class="panel-content" :style="{ height: isExpanded ? contentHeight + 'px' : '0px' }">
<slot></slot>
</div>
</div>
</template><script setup>
import { ref, nextTick } from 'vue'
const isExpanded = ref(false)
const panelRef = ref(null)
const contentHeight = ref(0)
const toggle = async () => {
isExpanded.value = !isExpanded.value
if (isExpanded.value) {
// 展开:先设为 auto 获取真实高度,再设为 0,再强制重绘,最后设为目标高度
await nextTick()
const el = panelRef.value
el.style.height = 'auto'
contentHeight.value = el.scrollHeight
el.style.height = '0px'
el.offsetHeight // 触发重绘
el.style.height = contentHeight.value + 'px'
} else {
// 收起:直接设为 0,自然过渡
panelRef.value.style.height = '0px'
}
}
</script><style scoped>
.panel-content {
overflow: hidden;
transition: height 0.3s ease;
}
</style>
注意事项
-
必须加
overflow: hidden:否则内容会溢出,动画失效; -
nextTick()不可省略:确保 DOM 已更新(尤其在v-if切换或动态内容渲染后); -
避免多次快速点击:可加
disabled或节流逻辑,防止 height 设置冲突; -
不建议用
max-height模拟:容易因预设值过大导致动画不匀速,或过小导致截断; -
若用
v-show或v-if配合:优先用v-show,因为v-if会销毁 DOM,ref 可能为空。
扩展:多个折叠项共存时
每个子项用独立 ref(如 ref="itemRefs" + :ref="(el) => itemRefs[index] = el"),配合数组管理高度状态,逻辑同上,只是循环处理即可。
不复杂但容易忽略
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










