Element Input组件分析小结
input组件相对来说复杂一点,我们先从它用到的一个工具库calctextareaheight.js
进行分析。
calctextareaheight.js
calctextareaheight.js使用来计算文本框的高度的,我们根据代码顺序从上往下进行分析。
hidden_style
hidden_style是一个常量,存储隐藏时候的css样式的。
const hidden_style = ` height:0 !important; visibility:hidden !important; overflow:hidden !important; position:absolute !important; z-index:-1000 !important; top:0 !important; right:0 !important `;
context_style
context_style也是一个常量,用来存储要查询的样式名。
const context_style = [ 'letter-spacing', 'line-height', 'padding-top', 'padding-bottom', 'font-family', 'font-weight', 'font-size', 'text-rendering', 'text-transform', 'width', 'text-indent', 'padding-left', 'padding-right', 'border-width', 'box-sizing' ];
calculatenodestyling
calculatenodestyling用来获取结点的某些样式。
function calculatenodestyling(node) { const style = window.getcomputedstyle(node); // 获取结点的计算后的样式,即实际渲染的样式 const boxsizing = style.getpropertyvalue('box-sizing'); // 获取 box-sizing 的值 // 上下的 padding 之和 const paddingsize = ( parsefloat(style.getpropertyvalue('padding-bottom')) + parsefloat(style.getpropertyvalue('padding-top')) ); // 上下的边框宽度和(其实是看上去的高度) const bordersize = ( parsefloat(style.getpropertyvalue('border-bottom-width')) + parsefloat(style.getpropertyvalue('border-top-width')) ); // 其他一些样式 const contextstyle = context_style .map(name => `${name}:${style.getpropertyvalue(name)}`) .join(';'); return { contextstyle, paddingsize, bordersize, boxsizing }; }
calctextareaheight
calctextareaheight是最终暴露出去的函数,用来计算文本域的高度。
export default function calctextareaheight( targetnode, // 要计算的结点 minrows = null, // 最小的行数 maxrows = null // 最大的行数 ) { if (!hiddentextarea) { // 来创建一个隐藏的文本域,所有的计算都是在这上面进行的 hiddentextarea = document.createelement('textarea'); document.body.appendchild(hiddentextarea); } // 获取结点一些样式值 let { paddingsize, bordersize, boxsizing, contextstyle } = calculatenodestyling(targetnode); // 设置相应的样式 hiddentextarea.setattribute('style', `${contextstyle};${hidden_style}`); // 设置内容,按优先级一次是 结点的 value, 结点的 placeholder, 以及空字符串 hiddentextarea.value = targetnode.value || targetnode.placeholder || ''; // 获取滚动高度 let height = hiddentextarea.scrollheight; if (boxsizing === 'border-box') { // 如果是 border-box,说明高度得加上边框 height = height + bordersize; } else if (boxsizing === 'content-box') { // 如果是 content-box,说明得减去上下内边距 height = height - paddingsize; } // 计算单行高度,先清空内容 hiddentextarea.value = ''; // 再用滚动高度减去上下内边距 let singlerowheight = hiddentextarea.scrollheight - paddingsize; if (minrows !== null) { // 如果参数传递了 minrows let minheight = singlerowheight * minrows; // 说明最少应当有这么多行的高度 if (boxsizing === 'border-box') { // 如果是 border-box,还得加上上下内边距和上下边框的宽度 minheight = minheight + paddingsize + bordersize; } height = math.max(minheight, height); // 取二者最大值 } if (maxrows !== null) { // 如果参数传递了 maxrows let maxheight = singlerowheight * maxrows; // 说明最多只能有这么多行的高度 if (boxsizing === 'border-box') { // 如果是 border-box,还得加上上下内边距和上下边框的宽度 maxheight = maxheight + paddingsize + bordersize; } height = math.min(maxheight, height); // 取二者最小值 } // 返回文本域应当设置的高度 return { height: height + 'px'}; };
input.vue
input组件较为繁琐,我们一点点分析。
生命周期
created
创建的时候会监听inputselect事件,并调用inputselect方法。
created() { this.$on('inputselect', this.inputselect); },
inputselect方法会调用refs上的input的原生的select方法,来选中该input。
methods: { inputselect() { this.$refs.input.select(); }, }
mounted
挂载的时候,会调用resizetextarea方法来设置文本域的大小。
mounted() { this.resizetextarea(); }
methods: { resizetextarea() { if (this.$isserver) return; // 如果是服务端渲染,直接返回,不进行下面的逻辑 var { autosize, type } = this; if (!autosize || type !== 'textarea') return; // 如果 autosize 是 false,或者当前不是文本域,也直接返回 const minrows = autosize.minrows; // 最少行数 const maxrows = autosize.maxrows; // 最大行数 this.textareastyle = calctextareaheight(this.$refs.textarea, minrows, maxrows); // 计算文本域的高度,并赋值 }, }
最外层
最外层是一个div,上面设置了一些动态的class。
<div :class="[ type === 'textarea' ? 'el-textarea' : 'el-input', size ? 'el-input--' + size : '', { 'is-disabled': disabled, 'el-input-group': $slots.prepend || $slots.append, 'el-input-group--append': $slots.append, 'el-input-group--prepend': $slots.prepend } ]"> </div>
type
type是一个prop,它默认设置为text,如果设置为textarea,表明当前是一个文本域。
props: { type: { type: string, default: 'text' }, }
size
size也是一个prop,用来设置输入框的大小,在textarea下无效。
props: { size: string, }
disabled
disabled也是一个prop,用来设置是否可用。
props: { disabled: boolean, }
prepend、append
这两个都是在设置输入框组的时候使用的,通过具名slot传入,分别放置于input的首和尾。
input
然后,根据type的不同使用v-if分别渲染input或者textarea,我们先分析input部分。
前置元素
前置元素直接通过具名slot传入。
<div class="el-input-group__prepend" v-if="$slots.prepend"> <slot name="prepend"></slot> </div>
input 图标
图标也是通过具名slot传入的,也可以通过prop中的icon传入图标名。
<slot name="icon"> <i class="el-input__icon" :class="'el-icon-' + icon" v-if="icon" @click="handleiconclick"> </i> </slot>
上面还绑定了一个handleiconclick的点击事件,它会触发click事件:
methods: { handleiconclick(event) { this.$emit('click', event); }, }
input
然后是最重要的input部分,上面大部分是prop,不进行讲解,其余的我们将一一讲解。
<input v-if="type !== 'textarea'" class="el-input__inner" :type="type" // 类型 :name="name" // 名字 :placeholder="placeholder" // 默认值 :disabled="disabled" // 是否禁用 :readonly="readonly" // 是否只读 :maxlength="maxlength" // 输入的最大长度 :minlength="minlength" // 输入的最小长度(暂时不支持) :autocomplete="autocomplete" // 自动补全 :autofocus="autofocus" // 自动聚焦 :min="min" // 允许输入的最小值(数字或者日期) :max="max" // 允许输入的最大值(数字或者日期) :form="form" // 绑定的表单(不是原生的) :value="currentvalue" // 输入值 ref="input" // 引用 @input="handleinput" // 输入事件 @focus="handlefocus" // 获得焦点事件 @blur="handleblur" // 失去焦点事件 >
value
value改变的时候会调用setcurrentvalue。
watch: { 'value'(val, oldvalue) { this.setcurrentvalue(val); } },
而setcurrentvalue是用来改变当前值的。
methods: { setcurrentvalue(value) { if (value === this.currentvalue) return; // 如果新旧值一致直接返回 this.$nexttick(_ => { this.resizetextarea(); // 下一个dom更新周期时,重新设置文本域大小 }); this.currentvalue = value; // 改变当前值 this.$emit('input', value); // 触发 input 事件 this.$emit('change', value); // 触发 change 事件 this.dispatch('elformitem', 'el.form.change', [value]); // 向父级的派发 el.form.change 事件 } }
handleinput
处理输入事件。
methods: { handleinput(event) { this.setcurrentvalue(event.target.value); // 改变当前值 }, }
handlefocus
handlefocus用来处理获得焦点的事件,会直接触发focus事件。
methods: { handlefocus(event) { this.$emit('focus', event); }, }
handleblur
handleblur用来处理失去焦点的事件。
methods: { handleblur(event) { this.$emit('blur', event); // 触发 blur 事件 this.dispatch('elformitem', 'el.form.blur', [this.currentvalue]); // 向父组件派发 el.form.blur 事件 }, }
loading
loading会根据计算属性validating来决定是否渲染。
computed: { validating() { return this.$parent.validatestate === 'validating'; } },
<i class="el-input__icon el-icon-loading" v-if="validating"></i>
后置元素
后置元素只能根据具名slot传入。
<div class="el-input-group__append" v-if="$slots.append"> <slot name="append"></slot> </div>
textarea
如果type设置为textarea则会渲染textarea,上面绑定的都和input类似,不再多说,多了一个textareastyle,是根据calctextareaheight计算出来的。
<textarea v-else class="el-textarea__inner" :value="currentvalue" @input="handleinput" ref="textarea" :name="name" :placeholder="placeholder" :disabled="disabled" :style="textareastyle" :readonly="readonly" :rows="rows" :form="form" :autofocus="autofocus" :maxlength="maxlength" :minlength="minlength" @focus="handlefocus" @blur="handleblur"> </textarea>
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
推荐阅读
-
深入分析element ScrollBar滚动组件源码
-
element-ui Tag、Dialog组件源码分析整理笔记(五)
-
element-ui button组件 radio组件源码分析整理笔记(一)
-
element-ui Upload 上传组件源码分析整理笔记(十四)
-
element-ui input组件源码分析整理笔记(六)
-
element-ui Steps步骤条组件源码分析整理笔记(九)
-
element-ui switch组件源码分析整理笔记(二)
-
element-ui inputNumber、Card 、Breadcrumb组件源码分析整理笔记(三)
-
element-ui Rate组件源码分析整理笔记(十三)
-
element-ui组件中input等的change事件中传递自定义参数