☰
uv-read-more 展开阅读更多组件源码解析:从版本演进看 rpx 高度修复与依赖优化
2026/10/15 20:27:25 网站建设 项目流程
  • 前端
  • UI组件
  • 跨平台

【免费下载链接】uv-ui

uv-ui 破釜沉舟之兼容vue3+2、app、h5、小程序等多端基于uni-app和uView2.x的生态框架,支持单独导入,开箱即用,利剑出击。

项目地址:https://gitcode.com/gh_mirrors/uv/uv-ui
点击查看免费下载

uv-read-more 是 uv-ui 生态中专门解决"长内容默认收起、点击展开全部"场景的组件,常见于文章详情、协议全文、商品介绍等长文本页面。本文以uni_modules/uv-read-more/changelog.md的版本演进为主线,结合组件源码、props 配置与仓库内演示页面,完整讲解其工作流程、全部配置参数、事件机制,以及 1.0.1 依赖优化与 1.0.2 rpx 高度修复背后的实现原理,读完即可在 uni-app 项目中独立使用并定位相关问题。

版本脉络与组件定位

该组件的变更记录非常精简,共经历三个版本,勾勒出一条清晰的演进路径:

  • 1.0.0(2023-05-10):发布uv-read-more,定位为"展开"组件;
  • 1.0.1(2023-05-16):优化组件依赖,修改后无需全局引入,组件导入即可使用;
  • 1.0.2(2023-09-13):修复全局设置 rpx 导致展开高度不对的 BUG。

组件在当前仓库中的完整位置为 组件实现、参数定义,演示页面位于 readMore.nvue,并在 components.config.js 中登记为ReadMore 展开示例入口。据 package.json 声明,组件版本为 1.0.2,最低要求 HBuilderX 3.1.0,宣称兼容 vue2/vue3、App(app-vue 与 app-nvue)、H5 以及微信、阿里、百度、字节跳动、QQ 等主流小程序平台,且插件不采集任何数据。

核心工作机制:如何实现"收起一段,展开全文"

从 uv-read-more.vue 的源码可以梳理出组件的核心工作流,其本质是一个"高度测量 + 状态切换"的过程。

内容高度测量(init → getContentHeight)

组件在mounted时调用init(),流程如下:

  1. 通过this.$uv.guid()为内容节点生成唯一 class(elId),避免多实例间选择器冲突;
  2. 在getContentHeight()中先await this.$uv.sleep(30)延时 30ms,等待节点渲染完成后再测量高度;
  3. 非 APP-NVUE 端通过this.$uvGetRect('.' + this.elId)(底层是 uni.createSelectorQuery)获取内容真实高度;
  4. APP-NVUE 端则使用uni.requireNativePlugin('dom')的getComponentRect原生方法获取高度,两种端侧实现互不干扰(条件编译分隔)。

高度对比决定是否展示按钮

拿到真实内容高度height后,组件执行关键判断:

// 判断高度,如果真实内容高度大于占位高度,则显示收起与展开的控制按钮 if (height > this.$uv.getPx(this.showHeight)) { this.isLongContent = true this.status = 'close' }

即:只有当内容真实高度大于showHeight(默认 400px)时,才认为这是"长内容",才会渲染底部的"展开阅读全文"按钮;否则内容完整展示,不出现任何切换入口。

收起与展开的状态渲染

模板中内容容器的高度由状态驱动:

<view class="uv-read-more__content" :style="{ height: isLongContent && status === 'close' ? $uv.addUnit(showHeight) : $uv.addUnit(contentHeight,'px'), textIndent: textIndent }" >
  • 收起状态(close):isLongContent === true时,容器高度被钳制为showHeight,配合外层overflow: hidden截断内容;
  • 展开状态(open):容器高度切换为实测的contentHeight(真实像素高度),全部内容可见。

切换事件

点击底部文字区域触发toggleReadMore():

toggleReadMore() { this.status = this.status === 'close' ? 'open' : 'close' // 如果toggle为false,隐藏"收起"部分的内容 if (this.toggle == false) this.isLongContent = false // 发出打开或者收齐的事件 this.$emit(this.status, this.name) }

注意两点实现细节:

  • 若toggle为false,展开后组件会彻底隐藏切换按钮(isLongContent被置为false),用户只能展开不能收起,适用于"详情页内查看更多后不再收回"的交互;
  • toggleReadMore向父组件分别派发open或close事件,并把name参数原样回传,方便多实例场景下区分事件来源。

参数详解:完整覆盖 props 定义

props.js 中定义了组件的全部配置项,实际使用时与uni.$uv?.props?.readMore合并(支持全局默认配置覆盖)。参数清单如下:

参数类型默认值说明
showHeightString | Number400内容超出此高度才显示展开按钮,单位 px
toggleBooleanfalse展开后是否显示"收起"按钮
closeTextString'展开阅读全文'收起状态下的提示文字
openTextString'收起'展开状态下的提示文字
colorString'#2979ff'提示文字与箭头图标的颜色
fontSizeString | Number14提示文字大小,单位 px
shadowStyleObject见下方说明收起时内容底部的渐变阴影样式
textIndentString'2em'段落首行缩进的字符数
nameString | Number''open/close事件回调中返回的标识参数

shadowStyle 的端差异处理

shadowStyle的默认值在 props 中通过条件编译区分端侧,这是该参数必须写在组件内、不能抽到外部公共配置中的原因——源码注释明确指出,外部 js 中 uni 无法准确识别当前是否处于 nvue 环境:

shadowStyle: { type: Object, default: () => ({ // #ifndef APP-NVUE backgroundImage: 'linear-gradient(-180deg, rgba(255, 255, 255, 0) 0%, #fff 80%)', // #endif // #ifdef APP-NVUE // nvue上不支持设置复杂的backgroundImage属性 backgroundImage: 'linear-gradient(to top, #fff, rgba(255, 255, 255, 0.5))', // #endif paddingTop: '100px', marginTop: '-100px' }) }

非 nvue 端使用从透明到白色的linear-gradient(-180deg, ...)渐变模拟"文字渐隐"效果;nvue 端因不支持复杂的backgroundImage语法,退化为to top的简化渐变。同时以paddingTop: 100px+marginTop: -100px的组合为按钮区腾出视觉空间。此外,展开状态下计算属性innerShadowStyle会返回空对象,即展开后自动去除阴影。

1.0.2 修复剖析:全局 rpx 配置下的高度一致性

changelog 中 1.0.2 记录的"修复全局设置 rpx,导致展开高度不对的 BUG",是理解组件单位体系的关键。它牵涉到 uv-ui 工具库中的两个核心函数:

单位工具函数:addUnit 与 getPx

addUnit 负责为数值补单位,其默认单位读取全局配置:

function addUnit(value = 'auto', unit = uni?.$uv?.config?.unit ? uni?.$uv?.config?.unit : 'px') { value = String(value) return number(value) ? `${value}${unit}` : value }

getPx 则负责把任意单位值统一折算为像素数值,其中rpx/upx会通过uni.upx2px换算:

function getPx(value, unit = false) { if (number(value)) { return unit ? `${value}px` : Number(value) } if (/(rpx|upx)$/.test(value)) { return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value))) } return unit ? `${parseInt(value)}px` : parseInt(value) }

全局单位配置位于 config.js:unit: 'px'是默认值,开发者可通过uni.$uv.setConfig({ config: { unit: 'rpx' } })(见 main.js 中被注释的示例)全局切换为 rpx。

修复点如何生效

从当前源码可以推断 1.0.2 的修复逻辑:

  1. 判断环节统一转 px:init()中height > this.$uv.getPx(this.showHeight)通过getPx把showHeight(无论传入数字、px还是rpx)统一换算成真实像素后再与实测高度比较,避免了"rpx 数值与 px 数值直接比较"的偏差;
  2. 渲染环节单位分离:模板中$uv.addUnit(showHeight)遵循全局unit配置(全局设为 rpx 时按 rpx 渲染,适配不同屏幕),而实测的contentHeight固定以'px'单位渲染($uv.addUnit(contentHeight, 'px')),因为它是createSelectorQuery返回的物理像素值,不能再经过 rpx 二次换算。

也就是说,修复后"比较"走像素统一、"展示"走各自合理单位,两条链路互不污染,从而保证全局切换 rpx 时展开高度依然准确。这一点在使用时同样值得注意:给showHeight传数值或 rpx 字符串均安全,组件会自行换算。

1.0.1 依赖优化:从全局引入到按需导入

changelog 中 1.0.1 记录"优化组件依赖,修改后无需全局引入,组件导入即可使用"。从当前仓库结构看,这一变化体现在两个方面:

uni_modules 依赖声明

package.json 中的uni_modules.dependencies明确声明了组件运行所需的三个依赖:

"uni_modules": { "dependencies": [ "uv-ui-tools", "uv-icon", "uv-text" ] }

即uv-read-more内部复用了 uv-ui 的图标组件uv-icon(箭头)、文字组件uv-text(提示语)以及基础工具包uv-ui-tools(mixin 与工具函数)。借助 uni_modules 的依赖机制,使用方只需引入uv-read-more,uni-app 会自动带上其声明的依赖,无需再手动全局注册 uv-ui 全家桶。

mixin 的解耦复用

组件脚本部分通过 mixin 组合能力接入基础设施(uv-read-more.vue):

import mpMixin from '@/uni_modules/uv-ui-tools/libs/mixin/mpMixin.js' import mixin from '@/uni_modules/uv-ui-tools/libs/mixin/mixin.js' import props from './props.js' export default { name: 'uv-read-more', mixins: [mpMixin, mixin, props], // ... }

mixin.js提供$uvGetRect、getParentData等通用能力,mpMixin.js提供小程序端适配,props.js承载参数定义。这种"工具包 + 图标 + 文本"的最小化依赖结构,正是"组件导入即可使用"的技术支撑——只要按 uni_modules 规范放置目录并声明 dependencies,即可独立安装到任意 uni-app 工程。

实战示例:在页面中接入 uv-read-more

仓库演示页 readMore.nvue 给出了一个完整可复用的接入范式:

<uv-read-more ref="uvReadMore" :showHeight="showHeight" toggle @open="open" @close="close"> <uv-parse :content="content" @load="load" :tag-style="tagStyle"></uv-parse> </uv-read-more>

对应脚本要点:

data() { return { showHeight: 200, tagStyle: { p: 'color: #606266; line-height: 24px;' } } }, methods: { load() { // 富文本渲染完成后,重新测量内容高度,确保高度判断基于完整渲染结果 this.$refs.uvReadMore.init() }, open(name) { console.log('open', name) }, close(name) { console.log('close', name) } }

该示例揭示了两个实战关键点:

  1. 内容可以任意嵌套:uv-read-more内部使用<slot></slot>承载任意内容(如uv-parse富文本、rich-text、普通文本段落等),组件只关心容器高度,不关心内容类型;
  2. 异步内容需手动刷新高度:当插槽内容由异步数据渲染(如富文本解析完成、网络加载完成)时,容器高度可能晚于mounted的测量时机。此时应通过ref调用组件暴露的init()方法强制重新测量,这正是示例中@load="load"后调用this.$refs.uvReadMore.init()的原因。

常规用法则简单得多,例如直接包裹一段富文本:

<uv-read-more :showHeight="300" toggle> <rich-text :nodes="articleContent"></rich-text> </uv-read-more>

使用建议与注意事项

综合源码实现,归纳几点实践建议:

  • 合理设置showHeight:它决定"多长才算长内容"的阈值,建议根据行高估算:设showHeight为 300 且行高 24px,则约显示 12.5 行后出现展开入口;
  • 根据交互选择toggle:需要"展开后可再次收起"时设toggle为true,否则保持默认false让展开即收尾;
  • 多实例场景用name区分:同一页面多个uv-read-more时,给每个实例传不同name,在open/close回调中即可辨别事件来源;
  • 异步内容记得调用init():插槽内容依赖异步渲染时,在内容加载完成事件中通过 ref 重新触发高度测量;
  • 单位体系安全:showHeight传数字、px或rpx字符串均可,组件内部统一转 px 比较;全局unit配置为 rpx 也不会影响展开高度判断(1.0.2 已修复)。

整体来看,uv-read-more是一个高度自洽的轻量组件:核心逻辑仅围绕"测量真实高度 → 与阈值比较 → 状态驱动高度切换"展开,参数面覆盖文字、颜色、阴影、缩进与事件回调,依赖面收敛到uv-icon、uv-text与uv-ui-tools三个最小单元,三个 changelog 版本恰好对应了"发布功能 → 解耦依赖 → 修复单位换算"的完整迭代,可作为研究 uv-ui 组件设计范式的一个小而完整的切入点。

  • 前端
  • UI组件
  • 跨平台

【免费下载链接】uv-ui

uv-ui 破釜沉舟之兼容vue3+2、app、h5、小程序等多端基于uni-app和uView2.x的生态框架,支持单独导入,开箱即用,利剑出击。

项目地址:https://gitcode.com/gh_mirrors/uv/uv-ui
点击查看免费下载

相关推荐

上一篇:Spring Data Redis安全最佳实践:保护你的数据和应用免受攻击的完整指南 🔒
下一篇:如何使用KeyPass自动填充功能:从设置到高效使用的完整教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询