☰
cube-ui Textarea 多行输入框组件详解:双向绑定、折叠展开与字数计数全配置
2026/9/25 3:58:24 网站建设 项目流程
  • 前端
  • UI组件
  • 移动开发

【免费下载链接】cube-ui

:large_orange_diamond: A fantastic mobile ui lib implement by Vue

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

导读

本文以 cube-ui(一个基于 Vue 构建的移动端 UI 组件库)中的Textarea多行输入框组件为主线,系统讲解其v-model双向绑定、基于"有内容 / 聚焦"的折叠与展开状态机、indicator计数标识(含remain/negative子配置与具名作用域插槽)以及原生属性透传等能力。读完本文,你将掌握从基础用法到源码级实现原理的完整知识,并可直接在真实表单场景中复刻出可运行的配置代码。

本文对应文档位于 document/components/docs/zh-CN/textarea.md,组件源码位于 src/components/textarea/textarea.vue。


组件概述:多行输入框的折叠与展开

Textarea是 cube-ui 在1.5.0版本新增的多行输入框组件,组件名注册为cube-textarea(注册逻辑见 src/modules/textarea/index.js)。

与普通textarea不同,它内置了一套状态机:根据"是否有内容、是否聚焦"在两个视觉状态间切换——

  • 折叠态:默认高度 40px,适合作为一行输入入口;
  • 展开态:高度 80px,提供更大的书写区域。

组件通过cube-textarea_expanded与cube-textarea_active两个 class 控制外观,并在样式层使用transition: height 200ms实现平滑的高度过渡动画,相关样式定义见 textarea.vue 的 style 段。

从源码看,折叠 / 展开的核心逻辑集中在 textarea.vue 与 textarea.vue:

data() { return { textareaValue: this.value, // 初始状态:forceExpand 恒展开;autoExpand 且默认有内容则展开 expanded: this.forceExpand ? true : (this.autoExpand ? !!this.value : false), isFocus: false } }

聚焦时强制展开(this.expanded = true);失焦时若内容为空则折叠回 40px。而forceExpand为true时组件始终保持展开,这个规则贯穿初始化和失焦两个入口。


基本用法:通过 v-model 双向绑定

cube-textarea使用v-model完成数据双向绑定,这也是文档给出的第一个示例:

<cube-textarea v-model="value"></cube-textarea>
export default { data() { return { value: '' } } }

底层实现中,组件维护内部状态textareaValue,并通过watch同时监听外部value与内部textareaValue(见 textarea.vue):

watch: { value(newValue) { this.textareaValue = newValue }, textareaValue(newValue) { this.$emit(EVENT_INPUT, newValue) // 触发 input 事件,完成 v-model 更新 // 非 forceExpand 且非聚焦状态下,若曾展开则自动折叠 if (!this.forceExpand && !this.isFocus && this.expanded) { this.expanded = false } } }

由此可以看到两个细节:其一,外部数据变化会同步进内部输入框;其二,当程序化地清空输入内容(例如表单重置)且组件未聚焦时,组件会自动回到折叠态——这正呼应了"根据是否有内容决定折叠/展开"的设计。

此外,组件混入了 src/common/mixins/input.js,其中定义了focus()/blur()实例方法及changeHander(透传原生change事件),下方实例方法一节会详细展开。


配置计数标识:indicator 的三种形态

计数标识是Textarea最具特色的能力。文档要求:indicator的值可以是false、true或配置对象,分别对应三种行为:

  • false:完全不显示右下角的计数标识;
  • true:等同于{ remain: true, negative: true },即显示"剩余可输入字数",且允许负值;
  • 对象:通过remain与negative两个布尔字段精细控制。

基本配置示例:

<cube-textarea v-model="value" indicator="indicator"></cube-textarea>
export default { data() { return { indicator: { negative: true, remain: true } } } }

indicator 子配置项

| 参数 | 说明 | 类型 | 可选值 | 默认值 | | - | - | - | - | - | | remain | 是否显示剩余字数,为false时显示已输入字数 | Boolean | true/false | true | | negative | 当remain为 true 时有效,是否允许出现负值(即允许超出 maxlength 后显示负数) | Boolean | true/false | true |

从源码看,textarea.vue 用indicatorConf计算属性将用户配置与默认值合并,并用count/remain两个计算属性得出展示数值:

const DEFAULT_INDICATOR = { negative: true, remain: true } indicatorConf() { let indicator = this.indicator if (typeof indicator === 'boolean') { indicator = {} } return Object.assign({}, DEFAULT_INDICATOR, indicator) }, count() { return this.textareaValue.length }, remain() { let diff = this.maxlength - this.count if (!this.indicatorConf.negative && diff < 0) { diff = 0 // negative 为 false 时,超限一律显示 0 } return diff }

值得注意的两点实现细节:

  1. negative: false的真正含义是"超限后仍显示 0 而非负数"——这在表单必填校验场景下可以避免出现刺眼的负数字;
  2. 默认计数标识文本由模板中的默认插槽内容渲染(见 textarea.vue),且仅在展开态(v-show="expanded")下可见。

自定义计数标识:具名作用域插槽 indicator(1.12.53+)

从1.12.53开始,组件支持使用具名作用域插槽indicator完全自定义右下角计数标识。插槽提供两个作用域值:remain(剩余可输入字数)和count(当前已输入字数)。

<cube-textarea v-model="text" placeholder="请您至少输入8个字(必填)" :maxlength="300" :auto-expand="true" > <span slot="indicator" slot-scope="{ remain }" class="cube-textarea-indicator">{{remain}}/300</span> <!-- 或者 vue2.6以上 --> <!-- <template #indicator="childValue"> <span class="cube-textarea-indicator">{{childValue.remain}}/300</span> </template> --> </cube-textarea>

插槽模板的实现位于 textarea.vue,它把remain/count通过作用域暴露出来,默认内容仅充当兜底:

<slot name="indicator" :remain="remain" :count="count"> <span v-if="indicator" v-show="expanded" class="cube-textarea-indicator">{{indicatorConf.remain ? remain : count}}</span> </slot>

在 example/pages/textarea.vue 的官方示例中,还演示了force-expand+ 自定义插槽的组合用法,将计数渲染为{{remain}}/{{maxlength}}的"已用/总量"样式,这在移动端输入场景中非常常见。


多项配置:原生属性透传

组件通过v-bind="$props"将声明的 props 直接透传到原生<textarea>上(见 textarea.vue),因此placeholder、maxlength、readonly、disabled、autofocus等原生能力开箱即用:

<cube-textarea v-model="value" :placeholder="placeholder" :maxlength="maxlength" :readonly="readonly" :disabled="disabled" :autofocus="autofocus" ></cube-textarea>
export default { data() { return { value: '', placeholder: '请输入内容', readonly: true, maxlength: 100, disabled: true, autofocus: true } } }

该行为在 test/unit/specs/textarea.spec.js 中有对应测试用例,断言透传后原生textarea的disabled、readOnly、autofocus属性均为true,可作为配置生效的直接证据。


Props 配置总览

| 参数 | 说明 | 类型 | 可选值 | 默认值 | | - | - | - | - | - | | v-model | 绑定的值 | String | - | 空 | | disabled | 禁用状态 | Boolean | true/false | false | | readonly | 只读状态 | Boolean | true/false | false | | maxlength | 最大输入长度 | Number | - | 60 | | placeholder | 占位文本 | String | - | 空 | | autofocus | 自动对焦 | Boolean | true/false | false | | indicator1.10.0| 计数标识配置 | Boolean/Object | true/false/{} | true | | autoExpand1.12.0| 为 true 且默认有内容时,默认展开 | Boolean | true/false | false | | forceExpand1.12.53| 为 true 时始终保持展开 | Boolean | true/false | false |

补充几点从源码确认的细节:

  • 组件还额外声明了cols、rows、wrap、required、dirname、form、name等原生属性(见 textarea.vue),这些同样会通过$props透传到原生元素上,类型声明见 types/components/Textarea.d.ts;
  • autoExpand的生效前提是"默认有内容":初始展开条件为autoExpand ? !!this.value : false,即只有初始value非空时才展开;
  • forceExpand优先级最高:一旦为true,无论内容多少、是否聚焦,组件始终处于 80px 展开态,watch与blur中的折叠分支都会被跳过。

插槽

| 名称 | 说明 | | - | - | | indicator1.12.53| 自定义右下角计数标识,作用域参数为remain(剩余字数)与count(已输入字数) |


事件

| 事件名 | 说明 | 参数 | | - | - | - | | focus | 输入框聚焦后触发;禁用状态下不触发 | e - 事件对象 | | blur | 输入框失焦后触发 | e - 事件对象 | | input | 绑定值变化时触发 | 更新后的绑定值 |

focus/blur的触发逻辑见 textarea.vue,input事件在textareaValue变化时通过$emit('input', newValue)抛出,这正是v-model得以工作的基石。此外,组件还会透传原生change事件(由 src/common/mixins/input.js 中的changeHander抛出)。


实例方法

| 方法名 | 说明 | | - | - | | focus1.12.10+| 获得焦点 | | blur1.12.10+| 离焦 |

这两个方法定义在 src/common/mixins/input.js,实现非常直接,通过$refs.input调用原生focus()/blur():

focus() { this.$refs.input.focus() }, blur() { this.$refs.input.blur() }

使用方式:this.$refs.textarea.focus()(需给<cube-textarea ref="textarea">设置 ref)。


源码级深入:样式主题与测试验证

主题变量:一键换肤的样式基础

Textarea的视觉样式全部通过 Stylus 主题变量驱动(定义见 src/common/stylus/theme/default.styl):

//textarea $textarea-color := $color-grey $textarea-bgc := $color-white $textarea-border-color := $color-row-line $textarea-focus-border-color := $color-orange $textarea-outline-color := $color-orange $textarea-placeholder-color := $color-light-grey-s $textarea-indicator-color := $color-light-grey-s

其中聚焦边框色$textarea-focus-border-color与组件聚焦态 classcube-textarea_active对应,展开态的 80px 高度由 textarea.vue 中的.cube-textarea_expanded定义,折叠态 40px 为默认高度。这意味着在定制主题时,只需替换主题变量即可整体改变输入框外观。

测试用例:行为契约的实证

仓库在 test/unit/specs/textarea.spec.js 中为Textarea建立了完整的行为契约,可直接用于验证本文前述结论:

  • 折叠/展开高度:失焦后高度保持 40px;聚焦后高度变为 80px,清空内容再失焦则恢复 40px(should not expand when blur、should expand when focus, fold when blur);
  • 计数展示:maxlength=60 时聚焦显示剩余56,输入 61 个字符后显示0,将indicator.negative设为false时超限仍显示0(should has remain when focus);
  • remain=false 显示输入字数:remain: false时计数显示已输入字符数4(should change value);
  • 原生属性透传:autofocus/readonly/disabled均正确落到原生元素(should support more native props);
  • autoExpand:初始有内容且autoExpand: true时挂载即带cube-textarea_expandedclass(should expand when autoExpand)。

这些用例既是组件稳定性的保障,也是读者理解各配置项实际效果的最快途径。


实战建议:组合配置要点

结合文档与示例页(example/pages/textarea.vue),给出几个高频组合场景:

  1. 必填长文本 + 剩余字数提示:auto-expand保证有初值时展开,:maxlength限制长度,默认indicator即可显示剩余字数;
  2. 超出限制也不显示负数:配置:indicator="{ remain: true, negative: false }",超限时计数固定显示 0;
  3. 自定义"已用/总量"计数:1.12.53+使用slot="indicator"配合作用域remain渲染{{remain}}/{{maxlength}},同时可令indicator为false隐藏默认计数;
  4. 始终展开的固定区域:force-expand适用于评论区这类固定高度的场景,避免折叠/展开跳动影响布局。

组件完整能力以源码为准,使用时请确认你的 cube-ui 版本不低于对应能力标注的版本号(例如插槽与forceExpand需 1.12.53+、focus/blur方法需 1.12.10+、autoExpand需 1.12.0+、indicator配置需 1.10.0+)。

  • 前端
  • UI组件
  • 移动开发

【免费下载链接】cube-ui

:large_orange_diamond: A fantastic mobile ui lib implement by Vue

项目地址:https://gitcode.com/gh_mirrors/cu/cube-ui
点击查看免费下载
上一篇:实战技巧:高效解决AKShare股票数据采集连接中断问题
下一篇:鼠标侧键定制终极指南:快速解锁隐藏效率神器

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

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

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

立即咨询