最近在阅读Element-UI的input组件源码时,发现其使用了composition事件:
<input
:tabindex="tabindex"
v-if="type !== 'textarea'"
class="el-input__inner"
v-bind="$attrs"
:type="type"
:disabled="inputDisabled"
:readonly="readonly"
:autocomplete="autoComplete || autocomplete"
:value="currentValue"
ref="input"
@compositionstart="handleComposition" // 注意这里!
@compositionupdate="handleComposition" // 注意这里!
@compositionend="handleComposition" // 注意这里!
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
@change="handleChange"
:aria-label="label"
>
印象里红皮书好像有提到过,但已经记不清有什么作用了,趁此机会学习下。
composition event,即复合事件,用于处理IME的输入序列。
IME(Input Method Editor,输入法编辑器)可以让用户输入在物理键盘上找不到的字符。
其实就是我们用中文输入法时,出现的显示中文的框:
composition event包括三个事件:
- compositonstart: 在IME的文本复合系统打开时触发,表示要开始输入例如(输入法出现的那一刻)
- compositionupdate: 在向输入字段中插入新字符时触发(使用输入法输入的过程中)
- compositionend: 在IME的文本复合系统关闭时触发,表示返回正常键盘输入状态(选中文字,输入法消失的那一刻)
那Element-UI为什么要使用composition event呢?
这其实跟composition event的作用有关,我们来看下compotion event与input event的触发顺序:
可以看到,首先输入法出现,触发了compositonstart事件,然后分别输入'w'和'o',先是触发compositionupdate事件,再是触发input事件。当然,如果选择了中文,会触发compositionend事件。
这里的重点是,在拼音还未转化成中文的时候,同时有两个输入框存在:
- 页面上的input输入框
- 输入法的输入框
两者分别会触发input和compositionupdate事件。这样问题就出现了,用过Element-UI或者类似UI库的同学都知道,input组件多数情况下都是配合着form组件使用的,既然是表单,那也离不开表单验证了。那么问题就在于,如果是通过input事件来触发验证的,输入的是字符,那倒没什么问题。可要是输入的是中文(或者其它需要组合拼出来的语言),比如,要输入'我',在拼音还没转换之前,网页输入框中的内容时'wo',也会触发验证,这并不是我们想要的!
因此,我们可以使用复合事件,通过一个变量来判断是否是在composition event中,是,就不去触发input事件。
当然,Element-UI也是这么做:
handleComposition(event) {
if (event.type === 'compositionend') {
this.isOnComposition = false;
this.currentValue = this.valueBeforeComposition;
this.valueBeforeComposition = null;
this.handleInput(event);
} else {
const text = event.target.value;
const lastCharacter = text[text.length - 1] || '';
this.isOnComposition = !isKorean(lastCharacter);
if (this.isOnComposition && event.type === 'compositionstart') {
this.valueBeforeComposition = text;
}
}
}
该方法是composition event的统一处理方法,this.isOnComposition用来判是否开启了输入法,在compositionend事件触发时,将this.isOnCompostion = false; 表明输入法已关闭,再去执行this.handleInput,即input事件的处理方法。
handleInput(event) {
const value = event.target.value;
this.setCurrentValue(value);
if (this.isOnComposition) return;
this.$emit('input', value);
}
在handleInput方法中,可以看到,若this.isOnComposition为true,是不会执行this.$('input', value);的,即不会向父组件派发input事件,如果父组件是form组件,也就不会去触发表单验证了。