在書寫petite-vue和Vue最舒服的莫過於通過@click
綁定事件,而且在移除元素時框架會幫我們自動解除綁定。省去了過去通過jQuery的累贅。而事件綁定在petite-vue中就是一個指令(directive),和其他指令類似。
深入v-on
的工作原理
walk
方法在解析模板時會遍歷元素的特性集合el.attributes
,當屬性名稱name
匹配v-on
或@
時,則將屬性名稱和屬性值壓入deferred
隊列的隊尾,噹噹前元素所有屬性綁定和v-modal
處理後以及子元素所有屬性綁定、v-modal
和事件綁定處理後再處理。
那問題來了,爲什麼要將事件綁定放到最後處理呢?
//文件 ./src/on.ts
const systemModifiers = ['ctrl', 'shift', 'alt', 'meta']
const modifiersGuards: Record<
string,
(e: Event, modifiers: Record<string, true>) => void | boolean
> = {
stop: e => e.stopPropagation(),
prevent: e => e.preventDefault(),
self: e => e.target !== e.currentTarget,
ctrl: e => !(e as KeyedEvent).ctrlKey,
shift: e => !(e as KeyedEvent).shiftKey,
alt: e => !(e as KeyedEvent).altKey,
meta: e => !(e as KeyedEvent).metaKey,
left: e => 'button' in e && (e as MouseEvent).button !== 0,
middle: e => 'button' in e && (e as MouseEvent).button !== 1,
right: e => 'button' in e && (e as MouseEvent).button !== 2,
/* @click.alt.shift 將分別匹配alt和shift兩個modifiers guards,當此時按alt+shift+ctrl時,兩個modifiers guards均通過。
* 而@click.alt.shift.exact 將分別匹配alt、shift和exact,當此時按alt+shift+ctrl時,前面兩個modifiers guards均通過,但最後的exact guard將返回true,不執行事件回調函數。
*/
exact: (e, modifiers) =>
systemModifiers.some(m => (e as any)[`${m}Key`] && !modifiers[m])
}
export const on: Directive({ el, get, exp, arg, modifiers }) => {
let handler = simplePathRE.test(exp)
? get(`(e => ${exp}(e)`)
: get(`($event => { ${exp} })`)
if (arg === 'vue:mounted') {
// 假如綁定的是生命週期函數mounted,但由於當前元素早已添加到DOM樹上,因此將函數壓入micro queue執行
nextTick(handler)
return
}
else if (arg === 'vue:unmounted') {
// 假如綁定的是生命週期函數unmounted,則返回cleanup函數
return () => handler()
}
if (modifiers) {
// 如果存在modifiers,則對事件綁定進行增強
if (arg === 'click') {
// @click.right 對應的DOM事件是contextmenu
if (modifiers.right) arg = 'contextmenu'
// @click.middle 對應的DOM事件是mouseup
if (modifiers.middle) arg = 'mouseup'
}
const raw = hanlder
handler = (e: Event) => {
if ('key' in e && !(hyphenate((e as KeyboardEvent).key) in modifiers)) {
/* 如果爲鍵盤事件,鍵不在沒有在modifiers中指定則不執行事件回調函數
* key值爲a、b、CapsLock等,hyphenate將CapsLock轉換爲caps-lock
*/
return
}
for (const key in modifiers) {
// 執行modifiers對應的邏輯,若返回true則不執行事件回調函數
const guard = modiferGuards[key]
if (guard && guard(e, modifiers)) {
return
}
return raw(e)
}
}
}
// 居然沒有返回cleanup函數??大家可以去貢獻代碼了哈哈
listen(el, arg, handler, modifers)
}
//文件 ./src/utils.ts
export const listen = (
el: Element,
event: string,
handler: any,
opotions?: any
) => {
el.addEventListener(event, handler, options)
}
總結
現在我們已經瞭解了v-bind
和v-on
的工作原理,後面我們一起看看v-modal
吧!