Vant TextEllipsis 组件实战指南:长文本省略、展开/收起与自定义省略位置
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
本指南以 Vant 移动端组件库中的TextEllipsis组件为对象,系统讲解如何在 Vue 3 项目中实现长文本的多行省略、展开/收起交互,以及从头/中/尾三个位置自定义省略方式。读完本文,你将掌握该组件的全部 Props、事件、实例方法、插槽与主题变量,并能基于源码理解其基于 DOM 克隆与二分查找的省略文本计算原理。
组件定位与引入
TextEllipsis是 Vant 提供的一个轻量文本省略组件,用于对超长文本进行省略展示,并原生支持展开/收起交互。该组件从vant >= v4.1.0版本开始提供,请在使用前确认依赖版本满足要求(dots属性自v4.2.0起可用,position属性自v4.6.2起可用,action插槽自v4.8.3起可用)。
组件的源码位于 packages/vant/src/text-ellipsis/TextEllipsis.tsx,入口文件 packages/vant/src/text-ellipsis/index.ts 通过withInstall将其包装为可全局注册的插件形式,同时声明了VanTextEllipsis全局组件类型:
declare module 'vue' { export interface GlobalComponents { VanTextEllipsis: typeof TextEllipsis; } }注册组件
推荐通过app.use全局注册,也可以按需引入(按需引入会自动注册样式):
import { createApp } from 'vue'; import { TextEllipsis } from 'vant'; const app = createApp(); app.use(TextEllipsis);关于组件注册的更多方式(如局部注册、自动按需导入),可参考 组件注册文档。
基础用法:单行省略
不传任何配置时,组件默认展示 1 行,超长内容尾部以省略号...截断:
<van-text-ellipsis :content="text" />export default { setup() { const text = 'Take your time and be patient. Life itself will eventually answer all those questions it once raised for you.'; return { text }; }, };当内容未超过设定行数时,组件不会渲染省略号与操作按钮,这一点由源码中的高度对比逻辑保证(详见下文「源码原理」一节),对应的测试用例text not exceeded也验证了短文本不会出现...(见 test/index.spec.tsx)。
展开/收起
通过expand-text与collapse-text两个属性分别指定「展开」与「收起」操作文案,点击后即可在完整文本与省略文本之间切换:
<van-text-ellipsis :content="text" expand-text="expand" collapse-text="collapse" />export default { setup() { const text = "The fleeting time of one's life is everything that belongs to a person. Only this thing truly belongs to you. Everything else is just a momentary pleasure or misfortune, which will soon be gone with the passing of time."; return { text }; }, };展开/收起状态的切换由实例方法toggle驱动:点击操作区会调用toggle()取反展开状态,同时触发click-action事件(事件参数为原生MouseEvent)。
自定义展示行数
通过rows属性控制省略前展示的行数,属性值支持数字或字符串:
<van-text-ellipsis rows="3" :content="text" expand-text="expand" collapse-text="collapse" />export default { setup() { const text = "That day, I turned twenty-one. In the golden age of my life, I was full of dreams. I wanted to love, to eat, and to instantly transform into one of these clouds, part alight, part darkened. It was only later that I understood life is but a slow, drawn-out process of getting your balls crushed. Day by day, you get older. Day by day, your dreams fade. In the end you are no different from a crushed ox. But I hadn't foreseen any of it on my twenty-first birthday. I thought I would be vigorous forever, and that nothing could ever crush me."; return { text }; }, };自定义省略位置
默认情况下省略发生在文本尾部(position="end")。position属性支持start与middle两个取值,分别实现「头部省略」与「中部省略」:
头部省略(position="start")
省略号出现在文本开头,保留结尾部分:
<van-text-ellipsis rows="1" :content="text" expand-text="expand" collapse-text="collapse" position="start" />中部省略(position="middle")
省略号出现在文本中间,同时保留开头与结尾部分:
<van-text-ellipsis rows="2" :content="text" expand-text="expand" collapse-text="collapse" position="middle" />提示:
middle位置在计算时对文本左右两侧分别做二分逼近,从而找到「前后各保留多少字符」的精确切分点,具体实现见下文「源码原理」中的middleTail函数。
自定义操作内容(action 插槽)
默认操作区渲染的是expand-text/collapse-text文本。若需要自定义按钮样式或文案(例如带图标的按钮),可使用action插槽,插槽作用域暴露{ expanded: boolean },用于区分当前是展开态还是收起态:
<van-text-ellipsis :content="text"> <template #action="{ expanded }"> {{ expanded ? 'Collapse' : 'Expand' }} </template> </van-text-ellipsis>export default { setup() { const text = 'Take your time and be patient. Life itself will eventually answer all those questions it once raised for you.'; return { text }; }, };需要说明的是:只有内容确实超出rows指定行数时,操作区才会被渲染(由hasAction状态控制)。此外,当使用action插槽时,源码会在组件挂载后额外执行一次nextTick(calcEllipsised)重算,原因是插槽内容(操作按钮)本身也占用高度,需要将其计入省略计算。测试用例should render action slot correctly对该行为做了快照验证(见 test/index.spec.tsx)。
API 总览
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| rows | 展示的行数 | number | string | 1 |
| content | 展示的文本内容 | string | - |
| expand-text | 展开操作文案 | string | - |
| collapse-text | 收起操作文案 | string | - |
dotsv4.2.0 | 省略号文本内容 | string | '...' |
positionv4.6.2 | 省略位置,可选startmiddle | string | 'end' |
源码中这些属性的默认值定义在 TextEllipsis.tsx:
export const textEllipsisProps = { rows: makeNumericProp(1), dots: makeStringProp('...'), content: makeStringProp(''), expandText: makeStringProp(''), collapseText: makeStringProp(''), position: makeStringProp('end'), };其中rows使用makeNumericProp包装,因此同时接受数字与字符串;dots可自定义省略号文本(如'……')。
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| click-action | 点击展开/收起时触发 | event: MouseEvent |
对应测试用例should emit click event after Expand/Collapse is clicked验证了点击操作区会恰好触发一次该事件(见 test/index.spec.tsx)。
Methods
通过ref获取组件实例后调用:
| 名称 | 说明 | 参数 | 返回值 |
|---|---|---|---|
| toggle | 切换展开状态 | expanded?: boolean | - |
toggle支持传入目标状态(true展开 /false收起),不传参时自动取反当前状态:
const toggle = (isExpanded = !expanded.value) => { expanded.value = isExpanded; };该方法通过useExpose暴露到组件实例上(见 TextEllipsis.tsx),TSX 测试中即通过(wrapper.vm as TextEllipsisInstance).toggle()驱动状态切换。
Slots
| 名称 | 说明 | 插槽参数 |
|---|---|---|
actionv4.8.3 | 自定义操作内容 | { expanded: boolean } |
Types
组件导出以下类型定义,便于在 TypeScript 项目中获得完整的类型提示:
import type { TextEllipsisProps, TextEllipsisInstance, TextEllipsisThemeVars, } from 'vant';TextEllipsisInstance为组件实例类型,配合ref使用toggle方法:
import { ref } from 'vue'; import type { TextEllipsisInstance } from 'vant'; const textEllipsisRef = ref<TextEllipsisInstance>(); textEllipsisRef.value?.toggle();类型定义位于 types.ts:TextEllipsisInstance由ComponentPublicInstance<TextEllipsisProps, TextEllipsisExpose>构造,TextEllipsisThemeVars声明了textEllipsisActionColor主题变量。
主题定制:CSS 变量
组件提供以下 CSS 变量,可用于自定义样式。全局主题定制可配合 ConfigProvider 组件 使用:
| 名称 | 默认值 | 说明 |
|---|---|---|
| --van-text-ellipsis-action-color | var(--van-blue) | 操作文字颜色 |
| --van-text-ellipsis-line-height | 1.6 | 文本行高 |
对应的样式定义在 index.less:
:root, :host { --van-text-ellipsis-line-height: 1.6; --van-text-ellipsis-action-color: var(--van-blue); } .van-text-ellipsis { line-height: var(--van-text-ellipsis-line-height); white-space: pre-wrap; overflow-wrap: break-word; &__action { cursor: pointer; color: var(--van-text-ellipsis-action-color); &:active { opacity: var(--van-active-opacity); } } }值得注意的是,文本容器使用了white-space: pre-wrap,因此组件会保留内容中的换行符;line-height变量不仅影响视觉,还直接参与省略计算(见下文),因此修改行高变量后组件会自动适配截断结果。
源码原理:基于 DOM 克隆与二分查找的省略计算
理解TextEllipsis的实现,有助于预判它在复杂布局(如动态字体、容器尺寸变化、KeepAlive 缓存)下的行为。核心计算流程全部位于 TextEllipsis.tsx:
1. 克隆容器进行无痕测量
组件并不直接修改真实 DOM,而是将根节点的完整计算样式拷贝到一个position: fixed; top: -9999px; z-index: -9999的隐藏容器中(cloneContainer),把content文本塞入后追加到document.body,在不可见区域完成高度测量,测量结束后立即移除。这样既不影响页面布局,又能精确复现真实渲染行高。
2. 计算最大允许高度
calcEllipsised中,最大高度按行数换算:
const maxHeight = Math.ceil( (Number(props.rows) + 0.5) * pxToNum(lineHeight) + pxToNum(paddingTop) + pxToNum(paddingBottom), );即(rows + 0.5)倍行高再加上上下内边距。pxToNum负责把lineHeight等px字符串解析为数字。当maxHeight < container.offsetHeight时判定内容超行,执行省略计算并渲染操作区;否则直接展示完整文本、不渲染操作区。
3. 二分查找精确截断点
尾部省略(position="end")使用二分逼近(calcEllipse中的tail函数):不断尝试「保留前 middle 个字符 + 省略号」,若高度仍超过maxHeight则向左收缩,否则向右扩张,最终收敛到满足高度约束的最大保留长度,保证省略号后不会出现被截断的半行字符。头部省略(position="start")逻辑对称,保留的是文本尾部。
4. 中部省略的双侧逼近
中部省略(position="middle")由middleTail函数实现:以文本中点为中心,对左半部分与右半部分同时做二分收缩(左侧收缩用floor、右侧用ceil),直到拼接后的「左段 + 省略号 + 右段」高度恰好满足约束,从而最大化保留开头与结尾的有效信息。
5. 响应式重算与 KeepAlive 适配
组件对windowWidth(来自 utils/dom.ts 的useWindowSize)以及content、rows、position的变化做了watch,尺寸或配置变化时自动重算省略文本。针对 KeepAlive 缓存场景(对应 vant-ui/vant#12445 提到的问题),组件在onActivated钩子中检查needRecalculate标记并重新计算——当挂载时容器尚未连接(!root.value.isConnected)会导致测量失败,此时延迟到组件被激活时再补齐计算,保证被 KeepAlive 缓存的页面恢复显示时省略状态依然正确(见 test/index.spec.tsx 中的对应测试)。
总结
TextEllipsis以极简的 API 覆盖了移动端长文本展示的核心诉求:默认单行省略、多行截断、头/中/尾三种省略位置、内置展开收起与自定义插槽,并通过 CSS 变量保持与 Vant 主题体系一致。在源码层面,它以「隐藏克隆容器测量 + 二分查找」的方式计算截断文本,兼顾了精度与性能,同时对窗口尺寸变化与 KeepAlive 激活场景做了针对性处理。若要深入了解完整 API 与示例,可继续阅读组件文档 README.zh-CN.md 与演示代码 demo/index.vue。
【免费下载链接】vantA lightweight, customizable Vue UI library for mobile web apps.项目地址: https://gitcode.com/GitHub_Trending/va/vant
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考