前言 前面我們簡單的瞭解了 vue 初始化時的一些大概的流程,這裡我們詳細的瞭解下具體的內容; 內容 這一塊主要圍繞init.ts中的initEvents進行剖析,初始化生命周期之後緊接著。 initEvents initEvents的方法位於scr/core/instance/events.ts中 ...
前言
前面我們簡單的瞭解了 vue 初始化時的一些大概的流程,這裡我們詳細的瞭解下具體的內容;
內容
這一塊主要圍繞init.ts
中的initEvents
進行剖析,初始化生命周期之後緊接著。
initEvents
initEvents
的方法位於scr/core/instance/events.ts
中;
export function initEvents(vm: Component) {
// 創建一個空對象存放_events
vm._events = Object.create(null)
// 創建一個生命周期監聽事件的標識屬性
// Hook Event 可以從組件外部為組件註入額外的生命周期方法
vm._hasHookEvent = false
// init parent attached events
// 獲取initInternalComponent(options合併時候)的父組件自定義事件
const listeners = vm.$options._parentListeners
if (listeners) {
// 進行事件綁定,將父級的事件綁定到當前組件上
updateComponentListeners(vm, listeners)
}
}
let target: any
function add(event, fn) {
target.$on(event, fn)
}
function remove(event, fn) {
target.$off(event, fn)
}
function createOnceHandler(event, fn) {
const _target = target
return function onceHandler() {
const res = fn.apply(null, arguments)
if (res !== null) {
_target.$off(event, onceHandler)
}
}
}
export function updateComponentListeners(
vm: Component,
listeners: Object,
oldListeners?: Object | null
) {
target = vm
// 更新事件|事件註冊
updateListeners(
listeners, // 父級事件
oldListeners || {},
add, // 處理$on
remove, // 處理$off
createOnceHandler, //處理$once
vm
)
target = undefined
}
update-listeners.ts
updateListeners
位於src/core/vdom/helpers/update-listeners.ts
下;
export function updateListeners(
on: Object,
oldOn: Object,
add: Function,
remove: Function,
createOnceHandler: Function,
vm: Component
) {
let name, cur, old, event
for (name in on) {
cur = on[name]
old = oldOn[name]
event = normalizeEvent(name)
// 如果當前事件屬性是否為undefined或者null,是在開發環境下會發出相應的警告
// isUndef 判斷屬性是否為undefined或null
if (isUndef(cur)) {
__DEV__ &&
warn(
`Invalid handler for event "${event.name}": got ` + String(cur),
vm
)
} else if (isUndef(old)) {
// 如果事件沒有fns屬性則調用createFnInvoker()進行定義
// 說明是第一次創建
if (isUndef(cur.fns)) {
cur = on[name] = createFnInvoker(cur, vm)
}
// 事件存在once標識則調用createOnceHandler() 重新定義該事件
if (isTrue(event.once)) {
cur = on[name] = createOnceHandler(event.name, cur, event.capture)
}
// 將事件添加到$on屬性上
add(event.name, cur, event.capture, event.passive, event.params)
} else if (cur !== old) {
old.fns = cur
on[name] = old
}
}
for (name in oldOn) {
if (isUndef(on[name])) {
// 格式事件,來獲取真實的事件名稱和修飾符聲明對象
event = normalizeEvent(name)
// 如果老節點不存在name對應的事件就進行移除$off
remove(event.name, oldOn[name], event.capture)
}
}
}
/**
* 返回一個閉包函數invoker(),參數是執行的回調函數
* 添加invoker.fns 屬性
* 調用invoker時會對invoker.fns進行判斷,如果是數組會進行迴圈遍歷調用invokeWithErrorHandling函數否則直接調用
* invokeWithErrorHandling
*
* @param fns
* @param vm
* @returns
*/
export function createFnInvoker(
fns: Function | Array<Function>,
vm?: Component
): Function {
function invoker() {
const fns = invoker.fns
if (isArray(fns)) {
const cloned = fns.slice()
for (let i = 0; i < cloned.length; i++) {
invokeWithErrorHandling(
cloned[i],
null,
arguments as any,
vm,
`v-on handler`
)
}
} else {
// return handler return value for single handlers
// 返回處理程式單個處理程式的返回值 || 可以監聽自定義事件函數內部的處理錯誤
return invokeWithErrorHandling(
fns,
null,
arguments as any,
vm,
`v-on handler`
)
}
}
invoker.fns = fns
return invoker
}
/**
* 具有緩存的字元串截斷函數
* 返回真實的事件名稱和修飾符聲明對象
*/
const normalizeEvent = cached(
(
name: string
): {
name: string
once: boolean
capture: boolean
passive: boolean
handler?: Function
params?: Array<any>
} => {
const passive = name.charAt(0) === '&'
name = passive ? name.slice(1) : name
const once = name.charAt(0) === '~' // Prefixed last, checked first
name = once ? name.slice(1) : name
const capture = name.charAt(0) === '!'
name = capture ? name.slice(1) : name
return {
name,
once,
capture,
passive
}
}
)