最近在閱讀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元件,也就不會去觸發表單驗證了。