欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Element Input组件分析小结

程序员文章站 2022-03-20 15:12:50
input组件相对来说复杂一点,我们先从它用到的一个工具库calctextareaheight.js进行分析。 calctextareaheight.js calc...

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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。