v-if配合transition是uni-app公告展开收起最稳妥方案,因v-show易致小程序端滚动错乱或点击无响应;需用fade/slide命名、避免自定义类名、初始isExpanded为false、展开内容用v-if包裹,并注意-webkit-box截断、catch:tap防冒泡、expandedIndex单状态管理及动画时间显式写ms。

uni-app里用v-if控制公告展开收起最稳妥
直接用v-show或修改max-height做过渡动画,在小程序端(尤其是微信)容易失真或卡顿,v-if配合transition是目前兼容性最好、行为最可控的方式。关键不是“能不能动”,而是“动完状态是否准确”——比如折叠后滚动位置错乱、再次点击无响应,基本都源于v-show残留的渲染节点或样式冲突。
实操建议:
- 给公告容器加
transition标签,name用fade或slide,别用自定义类名(H5和小程序对enter-active-class支持不一致) - 初始状态用
isExpanded: false,点击只改这个布尔值,不要同时操作height或line-clamp - 展开内容区域用
<view v-if="isExpanded">...</view>,避免v-show隐藏时仍参与布局计算 - 如果需要“展开后自动滚动到顶部”,在
$nextTick里调uni.pageScrollTo,否则v-if插入节点还没渲染完成
多行截断用display: -webkit-box而非line-clamp全局生效
line-clamp在H5和App端表现正常,但在微信/支付宝小程序里会被忽略——因为它们用的是WebView内核,但-webkit-line-clamp依赖特定渲染层,而小程序的view组件并不完全等价于div。真正能跨平台生效的是老式-webkit-box方案,且必须配合overflow: hidden和text-overflow: ellipsis。
实操建议:
- 折叠状态下的公告标题用以下样式:
.ellipsis-2 { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; text-overflow: ellipsis; } - 不要在
view上直接写style="line-clamp: 2",小程序编译后会丢弃 - 如果文字含换行符
\n,需先用replace(/\n/g, '<br>')
并配合v-html,但注意XSS风险;更安全的做法是用rich-text组件,它原生支持\n转行
点击区域小、误触多?用catch:tap阻止冒泡比stopPropagation可靠
公告列表常嵌套在scroll-view或swiper里,用户点“展开”按钮时,容易顺手触发外层滚动或轮播切换。用@tap.stop在H5有效,但在小程序里可能失效——因为事件捕获阶段和冒泡阶段的处理逻辑不同。
实操建议:
- 展开按钮统一用
catch:tap="toggleExpand",不用@tap.stop或@tap.prevent - 如果按钮是
text或icon等非view组件,外层必须包一层view并加catch:tap,否则事件根本传不到 - 避免在
scroll-view内部用scroll-y+click事件监听,它和catch:tap存在竞争,优先级难控
性能敏感场景下,别在v-for里重复绑定isExpanded
一个页面有10条公告,每条都维护自己的isExpanded,看似合理,但会导致数据响应式开销翻10倍——尤其当公告内容含rich-text或图片时,setData体积暴涨,微信小程序直接报Maximum call stack size exceeded。
实操建议:
- 用
expandedIndex: number | null代替数组isExpanded: boolean[],只允许单条展开 - 如果必须多条同时展开,改用
Map对象缓存状态:const expandedMap = new Map<number boolean>()</number>,避免响应式劫持整个数组 - 在
onReachBottom加载更多时,重置expandedIndex为null,否则新公告会继承旧状态 - 展开动画时间别设太长,
300ms是临界值,超过后用户明显感知卡顿,尤其低端安卓机
实际项目里最容易被忽略的是:小程序真机调试时transition的duration单位必须显式写ms(如300ms),漏掉就变成300s,整个页面卡死。











