深入理解Vue.js源碼之事件機(jī)制
寫(xiě)在前面
因?yàn)閷?duì)Vue.js很感興趣,而且平時(shí)工作的技術(shù)棧也是Vue.js,這幾個(gè)月花了些時(shí)間研究學(xué)習(xí)了一下Vue.js源碼,并做了總結(jié)與輸出。
文章的原地址:https://github.com/answershuto/learnVue。
在學(xué)習(xí)過(guò)程中,為Vue加上了中文的注釋https://github.com/answershuto/learnVue/tree/master/vue-src,希望可以對(duì)其他想學(xué)習(xí)Vue源碼的小伙伴有所幫助。
可能會(huì)有理解存在偏差的地方,歡迎提issue指出,共同學(xué)習(xí),共同進(jìn)步。
Vue事件API
眾所周知,Vue.js為我們提供了四個(gè)事件API,分別是$on](https://cn.vuejs.org/v2/api/#vm-on-event-callback),[$once,$off](https://cn.vuejs.org/v2/api/#vm-off-event-callback),[$emit。
初始化事件
初始化事件在vm上創(chuàng)建一個(gè)_events對(duì)象,用來(lái)存放事件。_events的內(nèi)容如下:
{ eventName: [func1, func2, func3] }
存放事件名以及對(duì)應(yīng)執(zhí)行方法。
/*初始化事件*/ export function initEvents (vm: Component) { /*在vm上創(chuàng)建一個(gè)_events對(duì)象,用來(lái)存放事件。*/ vm._events = Object.create(null) /*這個(gè)bool標(biāo)志位來(lái)表明是否存在鉤子,而不需要通過(guò)哈希表的方法來(lái)查找是否有鉤子,這樣做可以減少不必要的開(kāi)銷(xiāo),優(yōu)化性能。*/ vm._hasHookEvent = false // init parent attached events /*初始化父組件attach的事件*/ const listeners = vm.$options._parentListeners if (listeners) { updateComponentListeners(vm, listeners) } }
$on
$on方法用來(lái)在vm實(shí)例上監(jiān)聽(tīng)一個(gè)自定義事件,該事件可用$emit觸發(fā)。
Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component { const vm: Component = this /*如果是數(shù)組的時(shí)候,則遞歸$on,為每一個(gè)成員都綁定上方法*/ 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 /*這里在注冊(cè)事件的時(shí)候標(biāo)記bool值也就是個(gè)標(biāo)志位來(lái)表明存在鉤子,而不需要通過(guò)哈希表的方法來(lái)查找是否有鉤子,這樣做可以減少不必要的開(kāi)銷(xiāo),優(yōu)化性能。*/ if (hookRE.test(event)) { vm._hasHookEvent = true } } return vm }
$once
$once監(jiān)聽(tīng)一個(gè)只能觸發(fā)一次的事件,在觸發(fā)以后會(huì)自動(dòng)移除該事件。
Vue.prototype.$once = function (event: string, fn: Function): Component { const vm: Component = this function on () { /*在第一次執(zhí)行的時(shí)候?qū)⒃撌录N(xiāo)毀*/ vm.$off(event, on) /*執(zhí)行注冊(cè)的方法*/ fn.apply(vm, arguments) } on.fn = fn vm.$on(event, on) return vm }
$off
$off用來(lái)移除自定義事件
Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component { const vm: Component = this // all /*如果不傳參數(shù)則注銷(xiāo)所有事件*/ if (!arguments.length) { vm._events = Object.create(null) return vm } // array of events /*如果event是數(shù)組則遞歸注銷(xiāo)事件*/ 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] /*Github:https://github.com/answershuto*/ /*本身不存在該事件則直接返回*/ if (!cbs) { return vm } /*如果只傳了event參數(shù)則注銷(xiāo)該event方法下的所有方法*/ if (arguments.length === 1) { vm._events[event] = null return vm } // specific handler /*遍歷尋找對(duì)應(yīng)方法并刪除*/ let cb let i = cbs.length while (i--) { cb = cbs[i] if (cb === fn || cb.fn === fn) { cbs.splice(i, 1) break } } return vm }
$emit
$emit用來(lái)觸發(fā)指定的自定義事件。
Vue.prototype.$emit = function (event: string): Component { const vm: Component = this if (process.env.NODE_ENV !== 'production') { const lowerCaseEvent = event.toLowerCase() 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}".` ) } } let cbs = vm._events[event] if (cbs) { /*將類(lèi)數(shù)組的對(duì)象轉(zhuǎn)換成數(shù)組*/ cbs = cbs.length > 1 ? toArray(cbs) : cbs const args = toArray(arguments, 1) /*遍歷執(zhí)行*/ for (let i = 0, l = cbs.length; i < l; i++) { cbs[i].apply(vm, args) } } return vm }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Vue的事件響應(yīng)式進(jìn)度條組件實(shí)例詳解
這篇文章主要介紹了Vue的事件響應(yīng)式進(jìn)度條組件的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-02-02vue.js指令v-for使用以及下標(biāo)索引的獲取
今天小編就為大家分享一篇關(guān)于vue.js指令v-for使用以及下標(biāo)索引的獲取,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01解決Vue中 父子傳值 數(shù)據(jù)丟失問(wèn)題
這篇文章主要介紹了解決Vue中 父子傳值 數(shù)據(jù)丟失問(wèn)題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08前端+接口請(qǐng)求實(shí)現(xiàn)vue動(dòng)態(tài)路由
在Vue應(yīng)用中,結(jié)合前端和后端接口請(qǐng)求實(shí)現(xiàn)動(dòng)態(tài)路由,可根據(jù)用戶權(quán)限動(dòng)態(tài)生成路由,提高安全性與靈活性,本文就來(lái)介紹一下前端+接口請(qǐng)求實(shí)現(xiàn)vue動(dòng)態(tài)路由,感興趣的可以了解一下2024-09-09詳解Vue內(nèi)部怎樣處理props選項(xiàng)的多種寫(xiě)法
這篇文章主要介紹了詳解Vue內(nèi)部怎樣處理props選項(xiàng)的多種寫(xiě)法,詳細(xì)的介紹了props的使用的寫(xiě)法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-11-11vue 綁定使用 touchstart touchmove touchend解析
這篇文章主要介紹了vue 綁定使用 touchstart touchmove touchend解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03