Vue.js 源码事件机制 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
answershuto
V2EX    前端开发

Vue.js 源码事件机制

  •  
  •   answershuto
    answershuto 2017-09-27 19:34:24 +08:00 2429 次点击
    这是一个创建于 2943 天前的主题,其中的信息可能已经有所发展或是发生改变。

    写在前面

    因为对 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$once$off,[$emit]( https://cn.vuejs.org/v2/api/#vm-emit-event-… args)。

    初始化事件

    初始化事件在 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 } 

    关于

    作者:染陌

    Email: [email protected] or [email protected]

    Github: https://github.com/answershuto

    Blog:http://answershuto.github.io/

    知乎主页:https://www.zhihu.com/people/cao-yang-49/activities

    知乎专栏:https://zhuanlan.zhihu.com/ranmo

    掘金: https://juejin.im/user/58f87ae844d9040069ca7507

    osChina:https://my.oschina.net/u/3161824/blog

    转载请注明出处,谢谢。

    欢迎关注我的公众号

    3 条回复    2017-09-28 00:30:16 +08:00
    blanu
        1
    blanu  
       2017-09-27 22:34:39 +08:00 via iPhone
    我觉得这是非常无聊的文章
    fuermosi777
        2
    fuermosi777  
       2017-09-28 00:06:49 +08:00
    我同意一楼的看法
    Lisp
        3
    Lisp  
       2017-09-28 00:30:16 +08:00 via iPhone
    下拉最后,并不是好心的分享
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2484 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 05:35 PVG 13:35 LAX 22:35 JFK 01:35
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86