- 前端
- UI组件
- 跨平台
【免费下载链接】uv-ui
uv-ui 破釜沉舟之兼容vue3+2、app、h5、小程序等多端基于uni-app和uView2.x的生态框架,支持单独导入,开箱即用,利剑出击。
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(),流程如下:
- 通过
this.$uv.guid()为内容节点生成唯一 class(elId),避免多实例间选择器冲突; - 在
getContentHeight()中先await this.$uv.sleep(30)延时 30ms,等待节点渲染完成后再测量高度; - 非 APP-NVUE 端通过
this.$uvGetRect('.' + this.elId)(底层是 uni.createSelectorQuery)获取内容真实高度; - 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合并(支持全局默认配置覆盖)。参数清单如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
showHeight | String | Number | 400 | 内容超出此高度才显示展开按钮,单位 px |
toggle | Boolean | false | 展开后是否显示"收起"按钮 |
closeText | String | '展开阅读全文' | 收起状态下的提示文字 |
openText | String | '收起' | 展开状态下的提示文字 |
color | String | '#2979ff' | 提示文字与箭头图标的颜色 |
fontSize | String | Number | 14 | 提示文字大小,单位 px |
shadowStyle | Object | 见下方说明 | 收起时内容底部的渐变阴影样式 |
textIndent | String | '2em' | 段落首行缩进的字符数 |
name | String | 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 的修复逻辑:
- 判断环节统一转 px:
init()中height > this.$uv.getPx(this.showHeight)通过getPx把showHeight(无论传入数字、px还是rpx)统一换算成真实像素后再与实测高度比较,避免了"rpx 数值与 px 数值直接比较"的偏差; - 渲染环节单位分离:模板中
$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) } }该示例揭示了两个实战关键点:
- 内容可以任意嵌套:
uv-read-more内部使用<slot></slot>承载任意内容(如uv-parse富文本、rich-text、普通文本段落等),组件只关心容器高度,不关心内容类型; - 异步内容需手动刷新高度:当插槽内容由异步数据渲染(如富文本解析完成、网络加载完成)时,容器高度可能晚于
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的生态框架,支持单独导入,开箱即用,利剑出击。
相关推荐
Hyperf v2.1 版本更新深度解读:依赖升级、核心组件演进与关键修复全景
Hyperf v2.1 版本更新深度解读:依赖升级、核心组件演进与关键修复全景 本文基于 Hyperf 开源仓库的官方变更记录,系统梳理 v2.1 系列(v2.
后端Web框架微服务RPC框架异步编程从 uv-pep508 更新日志看 uv 的 PEP 508 依赖解析器:演进脉络与源码实现
从 uv pep508 更新日志看 uv 的 PEP 508 依赖解析器:演进脉络与源码实现 本文以 uv 仓库中的 crates/uv pep508/Chan
包管理器开发工具CLIuv-upload 上传组件版本演进与源码级解析:从 1.0.0 到 1.0.6 的修复脉络
uv upload 上传组件版本演进与源码级解析:从 1.0.0 到 1.0.6 的修复脉络 本文以 uv ui 仓库中 uni_modules/uv uplo
前端UI组件跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考