JavaScript

超轻量级php框架startmvc

深入理解Vue.js源码之事件机制

更新时间:2020-06-08 05:54:01 作者:startmvc
写在前面因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时

写在前面

因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出。

文章的原地址:https://github.com/answershuto/learnVue。

在学习过程中,为Vue加上了中文的注释https://github.com/answershuto/learnVue/tree/master/vue-src,希望可以对其他想学习Vue源码的小伙伴有所帮助。 可能会有理解存在偏差的地方,欢迎提issue指出,共同学习,共同进步。

Vue事件API

众所周知,Vue.js为我们提供了四个事件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上创建一个_events对象,用来存放事件。_events的内容如下:


{
 eventName: [func1, func2, func3]
}

存放事件名以及对应执行方法。


/*初始化事件*/
export function initEvents (vm: Component) {
 /*在vm上创建一个_events对象,用来存放事件。*/
 vm._events = Object.create(null)
 /*这个bool标志位来表明是否存在钩子,而不需要通过哈希表的方法来查找是否有钩子,这样做可以减少不必要的开销,优化性能。*/
 vm._hasHookEvent = false
 // init parent attached events
 /*初始化父组件attach的事件*/
 const listeners = vm.$options._parentListeners
 if (listeners) {
 updateComponentListeners(vm, listeners)
 }
}

$on

$on方法用来在vm实例上监听一个自定义事件,该事件可用$emit触发。


 Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
 const vm: Component = this

 /*如果是数组的时候,则递归$on,为每一个成员都绑定上方法*/
 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
 /*这里在注册事件的时候标记bool值也就是个标志位来表明存在钩子,而不需要通过哈希表的方法来查找是否有钩子,这样做可以减少不必要的开销,优化性能。*/
 if (hookRE.test(event)) {
 vm._hasHookEvent = true
 }
 }
 return vm
 }

$once

$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
 }

$off

$off用来移除自定义事件


Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
 const vm: Component = this
 // all
 /*如果不传参数则注销所有事件*/
 if (!arguments.length) {
 vm._events = Object.create(null)
 return vm
 }
 // array of events
 /*如果event是数组则递归注销事件*/
 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参数则注销该event方法下的所有方法*/
 if (arguments.length === 1) {
 vm._events[event] = null
 return vm
 }
 // specific handler
 /*遍历寻找对应方法并删除*/
 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用来触发指定的自定义事件。


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) {
 /*将类数组的对象转换成数组*/
 cbs = cbs.length > 1 ? toArray(cbs) : cbs
 const args = toArray(arguments, 1)
 /*遍历执行*/
 for (let i = 0, l = cbs.length; i < l; i++) {
 cbs[i].apply(vm, args)
 }
 }
 return vm
 }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

Vue.js 事件机制