前言
該文錄實(shí)例方法集
正文
eventsMixin
收錄事件相關(guān)實(shí)例方法励堡,我們知道其實(shí)它就是發(fā)布訂閱相關(guān)方法
vm.$on
訂閱方法
// 用于檢測(cè)是否是hook事件
const hookRE = /^hook:/
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
const vm: Component = this
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
this.$on(event[i], fn)
}
} else {
(vm._events[event] || (vm._events[event] = [])).push(fn)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
首先定義變量hookRE
用于判定當(dāng)前實(shí)例是否有注冊(cè)hook
事件
我們知道這個(gè)函數(shù)event
參數(shù)支持字符串和數(shù)組,所以先判斷訂閱的事件名是不是數(shù)組,是的話就遍歷遞歸即可翔悠,否的話就簡(jiǎn)單入隊(duì)到vm._events
這個(gè)事件中心
這里做個(gè)優(yōu)化其實(shí)就是設(shè)置一個(gè)實(shí)例屬性
_hasHookEvent
,只要判斷到這個(gè)實(shí)例對(duì)象有注冊(cè)了hook
事件就置為true
郁轻,這樣子就可以在callHook(vm, 'beforeCreate')
之類時(shí)不用callHook(vm, 'hook:beforeCreate')
泥兰,只要在callHook
判斷下_hasHookEvent
然后加個(gè)前綴(hook:
)即可
vm.$once
一次訂閱方法
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on() {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
這個(gè)其實(shí)就是在其回調(diào)觸發(fā)時(shí)取消該事件訂閱即可,所以我們把傳入的回調(diào)fn
做下劫持就是了on.fn = fn
這句使得我們?cè)?code>$off量承、$on可以通過(guò)參數(shù)on
取到原本傳入的fn
回調(diào)
vm.$off
取消訂閱方法
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
const vm: Component = this
// all
// this.$off()
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// array of events
// this.$off(['test1', 'test2'], fn)
if (Array.isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
this.$off(event[i], fn)
}
return vm
}
// specific event
const cbs = vm._events[event]
// 事件不存在
if (!cbs) {
return vm
}
// 要取消的回調(diào)不存在
if (!fn) {
vm._events[event] = null
return vm
}
if (fn) {
// specific handler
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
// cb.fn其實(shí)是給$once使用的
// 這里就是判斷下要退訂的fn和已注冊(cè)的是否一致
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
}
return vm
}
這里更多的是$off
的各種傳參兼容搬设,很簡(jiǎn)單不贅述
- 如果沒(méi)有提供參數(shù),則移除所有的事件監(jiān)聽(tīng)器
- 如果只提供了事件撕捍,則移除該事件所有的監(jiān)聽(tīng)器
- 如果同時(shí)提供了事件與回調(diào)拿穴,則只移除這個(gè)回調(diào)的監(jiān)聽(tīng)器
vm.$emit
觸發(fā)訂閱更新
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
if (process.env.NODE_ENV !== 'production') {
const lowerCaseEvent = event.toLowerCase()
// https://cn.vuejs.org/v2/guide/components-custom-events.html#%E4%BA%8B%E4%BB%B6%E5%90%8D
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(vm)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(event)}" instead of "${event}".`
)
}
}
// 取到事件回調(diào)
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
// 取參數(shù)
const args = toArray(arguments, 1)
for (let i = 0, l = cbs.length; i < l; i++) {
try {
cbs[i].apply(vm, args)
} catch (e) {
handleError(e, vm, `event handler for "${event}"`)
}
}
}
return vm
}
首先就是在非開(kāi)發(fā)環(huán)境下判斷下事件名的問(wèn)題,其實(shí)是事件名kebab-case vs camelCase 或 PascalCase
忧风。也就是HTML
不區(qū)分大小寫(xiě)默色,所以建議使用kebab-case
而不是駝峰命名法
然后就是取到事件回調(diào)以及取參數(shù),畢竟$emit
是可以傳參數(shù)的
值得注意的是這里的cbs
肯定是數(shù)組狮腿,這是在$on
里_events
賦值的邏輯
最后就是遍歷回調(diào)腿宰,執(zhí)行回調(diào)就是了
這里判斷下
cbs
是不是數(shù)組長(zhǎng)度大于1
是的話toArray
轉(zhuǎn)下,其實(shí)感覺(jué)沒(méi)這必要缘厢,cbs
必定是數(shù)組