3.30 Input 输入框

简介

输入框组件 Input 通过鼠标或键盘输入表单域内容,提供复合型型输入框,带搜索的输入框,还可以进行大小选择。 本文将分析其源码实现,耐心读完,相信会对您有所帮助。🔗 组件文档 Inputarrow-up-right 🔗 gitee源码arrow-up-right

更多组件剖析详见 👉 📚 Element 2 源码剖析组件总览arrow-up-right

模板HTML

组件的 props 声明,各属性功能描述详见官方文档Input#attributesarrow-up-right

组件根元素是一个类名为el-textareael-input的 div 元素,元素内容包含了三部分:

  1. 封装原生 input 控件实现单行文本输入框。

  2. 封装原生 textarea 控件多行文本输入文本域。

// packages\input\src\input.vue
<template>
  <div :class="[type === 'textarea' ? 'el-textarea' : 'el-input',]" >
    <!-- 单行文本输入框 -->
    <template v-if="type !== 'textarea'">
      <!-- 输入框前置内容 -->
      <div class="el-input-group__prepend"></div> 
      <!-- 表单输入控件 -->
      <input>
      <!-- 输入框头部内容 -->
      <span class="el-input__prefix"></span>
      <!-- 输入框尾部内容 -->
      <span class="el-input__suffix"></span>
      <!-- 输入框后置内容 -->
      <div class="el-input-group__append"></div> 
    </template>
    <!-- 多行文本输入的文本域 -->
    <textarea></textarea>
  </div>
</template>

组件根元素

  • 属性type值确定使用 el-textareael-input

  • 组件尺寸inputSize生成样式 el-input--medium/small/mini

  • 禁用状态inputDisabled 生成样式 is-disabled

  • 开启输入长度限制,输入超限时inputExceed生成样式 is-disabled

  • 复合型输入框样式el-input-groupel-input-group--append/prepend根据前置/后置插槽内容生成。

  • 头部/尾部样式el-input--prefixel-input--suffix根据插槽内容或功能属性值生成。

  • 添加了鼠标移入移出事件,使用内部属性 hover 记录元素悬停状态。

单行文本输入

单行文本输入框通过封装原生 input 控件实现,支持控件的原生属性。组件通过组合以下多种元素实现文本框 text或密码框password等复合型输入框功能:

  1. 输入框前置内容,提供具名插槽prepend,内容一般为标签或按钮。

  2. 原生 input 表单输入控件,添加了自定义事件。

  3. 输入框头部内容,可以通过 prefix-icon 或具名插槽prefix增加显示图标。

  4. 输入框尾部内容,可以通过 suffix-icon 或具名插槽suffix增加显示图标。该元素也用于展示输入框清空Icon、密码显隐切换Icon以及展示输入字数统计。

  5. 输入框后置内容,提供具名插槽append,内容一般为标签或按钮。

组件渲染效果如下:

image.png

输入框头部/尾部都提供了具名插槽用于增加显示图标,当然也可以传入文本等其他内容,但不建议这么做。

当设置头部/尾部内容时, input 输入框通过属性 padding 提供了 30px 的宽度区域用于内容展示。头部/尾部元素使用绝对布局,将内容偏移覆盖至 padding 区域。

image.png

以下示例将自定义文本传入插槽。

示例渲染出现内容覆盖 image.png

input 元素类型

组件默认使用 text 类型的input控件。当设置 showPassword可得到一个可切换显示隐藏的密码框,内部属性passwordVisible记录显隐状态,根据不同的状态使用 passwordtext 类型。

属性 type 值也可设置为其他原生input的type值arrow-up-right

使用其他原生类型时组件渲染效果,但是一般不建议这么使用!一般组件类库都会提供对应的更加丰富的功能组件,使用库组件页面样式风格更加统一,提升用户交互体验。

image.png

输入法编辑器(IME)事件

输入法在中文、日文和韩文等少数语言中使用。以中文拼音输入法为例,输入的过程大致可以分为组字(composition)提交(commit) 两阶段。比如想打“你好”两个字,会在输入框输入“nihao”的拼音,当输入第一个字母“n”时,组字过程就开始了。此时本地的 IME(input method editor) 软件(比如微软/搜狗拼音输入法)会为我们提供组字框候选列表的 UI 组件。

关于输入法事件更多介绍,请阅读 Web 键盘输入法应用开发指南 —— 输入法事件arrow-up-right

compositionstartcompositionupdatecompositionend是一组事件。

  1. 首先是使用拼音输入法开始输入汉字时 compositionstart 被触发,组字框和候选列表相应出现;

  2. 此后,每按一个新键,就会触发 compositionupdate,此时组字框和候选列表的内容也发生变化;

  3. 当选择了候选列表中的某个字或词,或者敲击空格(中文输入法),compositionend 事件会触发,表明输入被提交。

  4. 当取消输入时,比如使用鼠标单击页面空白处,就会终止当前输入,也会触发compositionend 事件。

属性isComposing值为 true表示用户正在输入。输入结束后(选择字词或者取消),调用方法handleInput,触发组件 input 事件。

input/change 事件

  • <input><select><textarea> 元素的 value 被修改时,会触发 input 事件。

  • 当用户更改<input><select><textarea> 元素的值并提交这个更改时,change 事件在这些元素上触发。基于表单元素的类型和用户对标签的操作的不同,change 事件触发的时机也不同。

对于文本输入元素,比如 <input type="text">,每当元素的 value 改变,input 事件都会被触,change 事件在控件失去焦点后才会触发。

对于需要使用输入法的语言, v-model 不会在输入法组合文字过程中得到更新,因为compositionend事件没触发是不会执行handleInput逻辑。

focus/blur 事件

元素获取或失去焦点时,调用事件监听方法,更新内部属性focused记录元素焦点状态,同时触发实例自定义 focusblur 事件。

provide/inject 依赖注入

在 Form 组件中,每一个表单域由一个 Form-Item 组件构成,表单域中可以放置各种类型的表单控件,包括 Input、Select、Checkbox、Radio、Switch、DatePicker、TimePicker等。

表单form和表单域form-item使用provide 选项指定给后代组件的状态,避免了 prop 逐级透传

组件 input 使用inject选项注入上层组件提供的数据,如尺寸、校验、禁用,用于组件内部状态的控制计算。

生命周期

在生命周期钩子createdmountedupdated中,添加监听事件,初始化组件状态。

组件对属性 valuenativeInputValuetype添加了侦听器,用于状态更新以及关联表单验证事件。

下面逐一介绍下代这些方法的功能和作用。

select()

方法 select 用于选中输入控件的所有内容。

setNativeInputValue()

方法setNativeInputValue用于设置原生控件的 value 属性,该属性时一个包含了文本框当前文字的DOMString。原生控件的value值默认是空字符串 ("").

计算属性nativeInputValue 用于将输入内容格式化成字符串。

在方法setNativeInputValue中使用nativeInputValue更新属性value值。

resizeTextarea()

方法resizeTextarea用来设置文本域的大小,这个讲解 <textarea>详细介绍。

updateIconOffset()

方法updateIconOffset 根据前置/后置内容元素的 offsetWidth,在水平方向移动头部/尾部内容元素。

v-model

指令v-model常用于在表单输入元素(<input><textarea><select>)创建双向绑定数据绑定。它会根据控件类型自动选取正确的方法来更新元素。

它会根据所使用的元素类型自动使用对应的 DOM 属性和事件组合:

  • 文本类型的 <input><textarea> 元素会绑定 value property 并侦听 input 事件;

  • <input type="checkbox"><input type="radio"> 会绑定 checked property 并侦听 change 事件;

  • <select> 会绑定 value property 并侦听 change 事件。

v-model 会忽略任何表单元素上初始的 valuecheckedselected attribute。它将始终将当前绑定的 JavaScript 状态视为数据的正确来源。你应该在 JavaScript 中使用data 选项来声明该初始值。

v-model 本质是个语法糖,以组件el-input为例

上面的代码其实等价于下面这段 (编译器会对 v-model 进行展开):

所以在组件 inputprops 选项里声明 value 时必须的,这也是文档中为什么会说属性 value/v-model 都是绑定值

当组件el-input属性 value 最新值需要更新到父组件属性searchText时,就会使用$emit触发实例 input 事件,实现双向绑定。

所以官方文档这句话 Input 为受控组件,它总会显示 Vue 绑定值 也就不难理解了。

单行文本输入 input

后置内容

后置内容不止用于展示图标,还提供了内容清空、密码显隐、输入文字统计以及表单验证状态图标等内容。

后置内容元素渲染由很多数据状态控制。

可清空

使用clearable属性即可得到一个可清空的输入框。 计算属性showClear根据组件状态、输入内容等判断功能是否开启。

图标绑定 click 事件 ,调用方法 clear ,更新v-model值,触发组件实例的changeclear等自定义事件。

密码框

使用show-password属性即可得到一个可切换显示隐藏的密码框。

图标绑定 click 事件 ,调用方法 handlePasswordVisible ,更新内部属性passwordVisible值,因为密码显隐是通过渲染不同类型 (textpassword) 的input控件实现,此时DOM会重新渲染,所以使用$nextTick,调用方法focus重新获取元素的焦点。

输入长度限制

通过设置 show-word-limit 属性来展示字数统计。只能对类型为 texttextarea 的输入框生效, 使用原生maxlength属性限制最大输入长度 。

使用了计算属性 textLengthupperLimit 显示了输入进度。

image.png

计算属性upperLimit 返回最大输入长度,使用$attrs获取原生属性 maxlength值。

计算属性textLength返回当前输入内容的长度

计算属性 inputExceed 判断是否输入超限,用于生成组件根元素的样式is-exceed

表单验证结果反馈图标

当组件在表单中使用,表单form设置属性status-icon为输入框添加了表示校验结果的反馈图标。

组件实现渲染效果如下: image.png

文本域 textarea

组件通过封装原生 textarea 控件实现多行文本输入文本域功能。组件做了封装统一处理,所以textarea 控件的属性/事件跟input控件是相似的。

控件缩放

控件textarea 控件通过绑定计算属性textareaStyle设置内联样式,实现控件的高度自适应、文本区大小可调整。

计算属性textareaStyle中使用了内部属性textareaCalcStyle和prop属性 resize

  • 属性textareaCalcStyle值为文本域的高度属性(heightmin-height)样式,在方法resizeTextarea中由文本域内容和配置项计算生成。

  • 属性 resize 控制文本区是否可调整大小。

    • none 元素不能被用户缩放。

    • both 允许用户在水平和垂直方向上调整元素的大小。

    • horizontal 允许用户在水平方向上调整元素的大小。

    • vertical 允许用户在垂直方向上调整元素的大小。

可自适应文本高度

方法 resizeTextarea用来改变文本域的高度大小的。实例挂载时会调用该方法,当实例类型、输入框的值改变时也会多次调用该方法。

通过设置 autosize 属性可以使得文本域的高度能够根据文本内容自动进行调整,并且 autosize 还可以设定为一个对象,指定最小行数和最大行数。

calcTextareaHeight.js

类库导出方法calcTextareaHeight,用于动态计算文本域的高度样式。

calculateNodeStyling()

方法 calculateNodeStyling 用于获取实例文本域元素节点的样式,并计算box-sizing相关属性值。

  • contextStyle 复制当前实例元素的样式用于创建隐藏文本域。通过 window.getComputedStyle() 获取元素计算后/渲染后的所有 CSS 属性的值,然后获取数组CONTEXT_STYLE中指定属性值生成样式对象并转化字符串。

  • boxSizing 获取元素box-sizing 属性值。

  • paddingSize 获取元素上下内边距和。

  • borderSize 获取元素上下边框宽度和。

calcTextareaHeight()

方法 calcTextareaHeight通过创建一个跟实例元素一样的临时文本域,用于计算出自适应高度。

  1. 创建临时文本域元素 hiddenTextarea,获取实例元素的内容和样式值、内容赋值给临时元素,使其作为实例元素的复制镜像。通过样式 HIDDEN_STYLE 用于隐藏临时元素使其不可见。

  2. 获取元素内容高度scrollHeight,根据不同box-sizing 属性计算出元素总高度。

  3. 计算出单行文本行高度singleRowHeight

  4. 如果设置minRows,计算出元素属性min-height值。

  5. 如果设置maxRows,计算出元素最大高度,实际 height 不能超过最大高度。

  6. 清除临时文本域元素。

  7. 返回计算结果,格式 { height:20px }{ height:20px; minHeight:20px; }

属性scrollHeight是一个元素内容高度,包括由于溢出导致的视图中不可见内容。包括元素的 padding,但不包括元素的 border 和 margin。

属性 box-sizing 定义了如何计算一个元素的总宽度和总高度。

  • content-box 默认值,标准盒子模型。widthheight 只包括内容的宽和高,不包括边框(border),内边距(padding),外边距(margin)。

    • width = 内容的宽度

    • height = 内容的高度

  • border-box widthheight 属性包括内容,内边距和边框,但不包括外边距。

    • width = border + padding + 内容的宽度

    • height = border + padding + 内容的高度

样式实现

组件样式源码 packages\theme-chalk\src\input.scss 使用混合指令嵌套生成组件样式。

混合指令scroll-bar定义如下:

📚参考&关联阅读

"getComputedStyle",MDNarrow-up-right "CSS/box-sizing",MDNarrow-up-right "Element/scrollHeight",MDNarrow-up-right "表单输入绑定",vuejsarrow-up-right "input_event",MDNarrow-up-right "change_event",MDNarrow-up-right "Input/text",MDNarrow-up-right "String",MDNarrow-up-right

最后更新于