
1. 多行文本展开收起功能的常见场景在Web开发中处理长文本内容展示是个高频需求。特别是新闻类、博客类、商品详情页等场景经常需要控制大段文字的显示范围。想象一下电商平台的商品描述在列表页只需要展示前两行文字点击查看更多才展开完整描述或者社交媒体的评论区域过长的评论默认折叠用户主动点击才展开全文。这种交互模式有三大优势节省页面空间避免信息过载提升页面加载性能减少DOM渲染压力给予用户控制权按需查看内容2. Vue实现方案选型与对比2.1 纯CSS方案及其局限性最简单的实现方式是使用CSS的text-overflow和line-clamp属性.truncate { display: -webkit-box; -webkit-line-clamp: 3; /* 限制行数 */ -webkit-box-orient: vertical; overflow: hidden; }但这种方法存在明显缺陷无法动态调整显示行数兼容性问题特别是老版本IE缺少平滑的展开/收起动画效果2.2 JS计算方案的核心思路更可靠的方案是通过JavaScript计算文本高度获取文本容器的实际高度设置最大显示高度阈值动态切换CSS类控制显示状态添加过渡动画提升体验3. Vue组件化实现详解3.1 基础组件结构创建一个ExpandableText.vue组件template div classexpandable-container div refcontent classcontent :class{ collapsed: isCollapsed } :style{ maxHeight: isCollapsed ? maxHeight px : none } slot/slot /div button v-ifshowToggle classtoggle-btn clicktoggle {{ isCollapsed ? 展开 : 收起 }} /button /div /template3.2 核心逻辑实现export default { props: { lines: { type: Number, default: 3 }, lineHeight: { type: Number, default: 24 // 假设默认行高24px } }, data() { return { isCollapsed: true, showToggle: false, maxHeight: this.lines * this.lineHeight } }, mounted() { this.checkOverflow(); window.addEventListener(resize, this.checkOverflow); }, beforeDestroy() { window.removeEventListener(resize, this.checkOverflow); }, methods: { toggle() { this.isCollapsed !this.isCollapsed; }, checkOverflow() { this.$nextTick(() { const content this.$refs.content; this.showToggle content.scrollHeight this.maxHeight; }); } } }3.3 样式优化与动画.expandable-container { position: relative; } .content { overflow: hidden; transition: max-height 0.3s ease; line-height: 24px; /* 与JS中的lineHeight保持一致 */ } .toggle-btn { background: none; border: none; color: #1890ff; cursor: pointer; padding: 4px 0; font-size: 14px; }4. 高级功能扩展4.1 动态行高计算实际项目中行高可能不固定改进方案mounted() { // 获取实际行高 const tempSpan document.createElement(span); tempSpan.innerText 测; this.$refs.content.appendChild(tempSpan); this.lineHeight tempSpan.getBoundingClientRect().height; this.$refs.content.removeChild(tempSpan); this.maxHeight this.lines * this.lineHeight; this.checkOverflow(); }4.2 响应式设计考虑添加响应式断点处理props: { responsiveLines: { type: Object, default: () ({ mobile: 2, tablet: 3, desktop: 4 }) } }, methods: { updateLinesByViewport() { const width window.innerWidth; if (width 768) { this.maxHeight this.responsiveLines.mobile * this.lineHeight; } else if (width 992) { this.maxHeight this.responsiveLines.tablet * this.lineHeight; } else { this.maxHeight this.responsiveLines.desktop * this.lineHeight; } this.checkOverflow(); } }4.3 自定义插槽与国际化支持更灵活的按钮定制template !-- ...其他代码... -- button v-ifshowToggle clicktoggle slot nametoggle :isCollapsedisCollapsed {{ isCollapsed ? $t(expand) : $t(collapse) }} /slot /button /template5. 性能优化与边界处理5.1 防抖处理窗口resize事件import { debounce } from lodash-es; export default { methods: { checkOverflow: debounce(function() { // 原有逻辑 }, 200) } }5.2 内容变化监听watch: { $slots.default: { handler() { this.checkOverflow(); }, deep: true } }5.3 SSR兼容方案对于服务端渲染mounted() { if (typeof window ! undefined) { this.init(); } }6. 实际项目中的经验总结字体加载问题在自定义字体加载完成前计算的行高可能不准确解决方案document.fonts.ready.then(() { this.checkOverflow(); });动态内容处理对于异步加载的内容需要手动触发重新计算// 父组件中 this.$refs.expandableText.checkOverflow();无障碍访问添加ARIA属性提升可访问性button :aria-expanded!isCollapsed :aria-controlscontentId {{ isCollapsed ? 展开 : 收起 }} /button移动端优化添加touch事件支持防止点击延迟import fastclick from fastclick; mounted() { fastclick.attach(this.$el); }这个组件在实际项目中已经过多次迭代处理了各种边界情况。关键是要理解其核心原理通过比较内容的实际高度与设定的最大高度决定是否显示切换按钮。Vue的响应式特性让状态管理变得非常简单组件化的思想也使得这个功能可以轻松复用到项目的各个地方。