專註 Vue 源碼分享,為了方便大家理解,分為了白話版和 源碼版,白話版可以輕鬆理解工作原理和設計思想,源碼版可以更清楚內部操作和 Vue的美,喜歡我就關注我的公眾號,公眾號的文章,排版更好看
如果你覺得排版難看,請點擊下面鏈接 或者 關注公眾號
上一篇文章,我們大概講了所有表單元素的雙綁原理,但是仍然有兩個特殊的表單元素,是要多更多處理的,也不可能放在一篇文章說完,今天,我們說的是 input 的特殊處理的地方
而 input 有什麼特殊處理的地方呢?
1、預輸入延遲更新
2、range 類型的 input
3、v-model.lazy
4、v-model.trim、v-model-number
先來看看Vue 給 input 正常綁定的回調
input: function($event) { if ($event.target.composing) return; name = $event.target.value }
看到我標紅的地方,這句話就是完成預輸入延遲更新的重點
當composing=true時,事件回調不會走到下面的更新操作,而 Vue 正式通過這個標誌位,判斷現在是否是預輸入而確定是否需要實時更新
首先,Vue 會為 input 或者 textarea 綁定以下事件
開始講解這三個事件了
1、compositionstart
首先,compositionstart 會在 input 事件觸發之前 觸發
but!你打一些直接輸入的字元,是不會觸發 compositionstart 的,只會觸發 input
只有打預輸入的字元才會觸發,比如 輸入拼音,不行看圖
輸入普通字元
預輸入延遲更新下,輸入拼音
取消預輸入延遲更新,輸入拼音
看完上面的動圖,預輸入延遲更新什麼用,估計你心裡也有點逼數了吧?
為什麼要做預輸入延遲更新?
如果不做!在輸入拼音的時候,每打一個拼音字母都會觸發 input 事件,但是我們根本還沒往表單中寫入我們預想中的東西
而此時觸發 input 事件沒有任何意義,因為還不是我們要輸入的值,這是一個浪費的操作
剛好,compositionstart 在 input 之前觸發,而且只會預輸入才觸發
所以!就可以通過一個標誌位來控制 input 回調導致的更新就再好不過了!
怎麼做呢?看 compositionstart 回調源碼
function onCompositionStart(e) { e.target.composing = true; }
在打完預輸入的字元之後,會觸發
在預輸入延遲更新中起什麼作用呢?
預輸入結束,肯定是設置 composing 為 false了,看源碼
function onCompositionEnd(e,eventname) { if (!e.target.composing) { return } e.target.composing = false; trigger(e.target, input); }
還會 手動觸發 input 事件去 執行更新操作哦
trigger 是什麼?也是很簡單的,值得收藏的源碼,不解釋了
function trigger(el, type) { var e = document.createEvent(HTMLEvents); e.initEvent(type, true, true); el.dispatchEvent(e); }
3、change
為了兼容Safari<10.2 等那些 不會觸發 compositionend 的瀏覽器(Vue自己注釋說的,我沒有測過),於是監聽 change事件,來代替 compositionend 的功能
change 的回調 和 compositionend 的回調是一樣的,因為只是一個備胎功能
4、 他們在哪裡開始綁定這些事件呢?
你應該必須知道,指令都是有生命鉤子函數的,而這幾個事件正是在 inserted 鉤子中進行綁定的
Vue 官方文檔說明 inserted
看下 inserted 鉤子函數
function inserted(el, binding, vnode, oldVnode) {
// isTextInputType判斷 input 是不是 text,number,password,search,email,tel,url其中一個 if (vnode.tag === textarea || isTextInputType(el.type)) { el._vModifiers = binding.modifiers; // 如果設置 v-model.lazy,那麼不處理 預輸入的問題 if (!binding.modifiers.lazy) { el.addEventListener(compositionstart, onCompositionStart); el.addEventListener(compositionend, onCompositionEnd); el.addEventListener(change, onCompositionEnd); } } }
TIP
inserted 鉤子中,還處理了 select ,但是這裡是input的版塊,所以去掉了,放在下篇文章講
為了兼容 IE,所以在解析的時候,先保存的是 __r 事件,後面開始綁定的時候,判斷瀏覽器而決定使用什麼事件
function genDefaultModel( el, value, modifiers
){ var type = el.attrsMap.type; var ref = modifiers || {}; var lazy = ref.lazy;
// 這裡省略了lazy 的判斷啦 var event = type === range ? "__r" :input;
code = "if($event.target.composing)return;" + value+"=$event.target.value"; addProp(el, value, ("(" + value + ")")); addHandler(el, event, code, null, true); }
看到我加藍加粗的地方,就是為 range 特別處理的地方,綁定 __r 事件
判斷瀏覽器,轉換事件的函數updateDOMListeners源碼
function updateDOMListeners(oldVnode, vnode) {
var on = vnode.data.on if (isDef(on["__r"])) { var event = isIE ? change: input; on[event] = [].concat(on["__r"], on[event] || []); delete on["__r"]; } for (name in on) { vnode.elm.addEventListener(name, on[name]); } }
當你的 v-model 設置了 lazy 的時候,會綁定 change 而不是 input,延時更新的意思
){
var ref = modifiers || {}; var lazy = ref.lazy;
// 省略了 range 類型的判斷 var event = lazy ? change :input; addHandler(el, event, code, null, true); }
我們都知道,為了輸入實時響應,vue 默認為 input 等輸入類型的 表單 綁定 input 事件,讓 input
如果你設置延遲更新,就是相當於你改變了內容,然後失去焦點才觸發
如果你給 v-model 設置了值過濾,像 trim 去掉首尾空格,number 把值變成數字
function genDefaultModel( el, value, modifiers ){
var ref = modifiers || {}; var number = ref.number; var trim = ref.trim;
// 去首尾空格 if (trim) { valueExpression = "$event.target.value.trim()"; }
// 轉成數字 if (number) { valueExpression = "_n(" + valueExpression + ")"; }
code = "if($event.target.composing)return;" + value+"="+valueExpression;
addProp(el, value, ("(" + value + ")")); addHandler(el, "input", code, null, true);
if (trim || number) { addHandler(el, blur, $forceUpdate()); }
}
對於 trim 和 number,Vue 會對錶單值做處理,你可以看到源碼中
trim:值會調用 trim 方法
number:會調用 _n 轉換成數字方法
看下最終的回調
function($event){ if($event.target.composing)return; name=_n($event.target.value); }
function($event){ if($event.target.composing)return; name=$event.target.value.trim(); }
這兩個鬼東西,還會額外綁定一個事件 blur
看下回調 $forceUpdate,這個函數作用是強制更新頁面
為什麼要更新頁面?給個動圖看好吧
我設置了 trim,然後輸入的時候,故意多加幾個空格,然後失去焦點(觸發設置的 blur),再點發現空格不見了。因為失去焦點之後被強制更新了一波
嗯,這就是 $forceUpdate 的作用,把頁面上的顯示值也過濾一遍
【Vue原理】Vue源碼閱讀總結大會 - 序
【Vue原理】學會調試Vue源碼
【Vue原理】響應式原理 - 白話版
【Vue原理】Props - 白話版
【Vue原理】月老Computed - 白話版
【Vue原理】Watch - 白話版
【Vue原理】Mixin - 白話版
【Vue原理】Methods - 源碼版
希望你認真閱讀後,能豁然開朗。如果你懂了,我的目的也達到了,如果你還不懂,可以留言告訴我哦,百分百回復好吧
當然如果本文有任何描述不當的問題,歡迎後台聯繫本人
如果你能轉發一下,就更好啦,技術人交流 只用文章
TAG:Vue.js | 前端開發 | 前端框架 |