前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Vue 源码解析 (四)初始化事件流程

Vue 源码解析 (四)初始化事件流程

作者头像
公众号---人生代码
发布2021-05-31 16:23:16
发布2021-05-31 16:23:16
36300
代码可运行
举报
文章被收录于专栏:人生代码人生代码
运行总次数:0
代码可运行

initEvents

  • vm 出现 _events 对象
  • vm 出现 _hasHookEvent 表示是否存在 hook 事件
  • 初始化 updateComponentListeners
代码语言:javascript
代码运行次数:0
复制
export function initEvents (vm: Component) {
  vm._events = Object.create(null)
  vm._hasHookEvent = false
  // init parent attached events
  const listeners = vm.$options._parentListeners
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }
}

add

添加事件,内部使用 $on 监听绑定事件

代码语言:javascript
代码运行次数:0
复制
function add (event, fn) {
  target.$on(event, fn)
}

remove

解绑事件,内部使用 $off 解绑事件

代码语言:javascript
代码运行次数:0
复制
function remove (event, fn) {
  target.$off(event, fn)
}

createOnceHandler

创建只执行一次的 once 事件,返回一个函数体

代码语言:javascript
代码运行次数:0
复制
function createOnceHandler (event, fn) {
  const _target = target
  return function onceHandler () {
    const res = fn.apply(null, arguments)
    if (res !== null) {
      _target.$off(event, onceHandler)
    }
  }
}

updateComponentListeners

  • 更新前 target = vm
  • 更新事件
  • 更新后 target = null
代码语言:javascript
代码运行次数:0
复制
export function updateComponentListeners (
  vm: Component,
  listeners: Object,
  oldListeners: ?Object
) {
  target = vm
  updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)
  target = undefined
}

eventsMixin

Vue.prototype.$on

  • 判断要监听的事件是不是数组,如果是,逐个监听
  • 否则,vm._events 保存事件
  • 检测到魔板存在 hook 事件,则把 _hasHookEvent 设置为 true
代码语言:javascript
代码运行次数:0
复制
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++) {
        vm.$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
  }

以下代码会执行上面的代码:

代码语言:javascript
代码运行次数:0
复制
<keep-alive>
   <comp-a @hook:updated="hookUpdated"           @hook:mounted="hookUpdated" />
</keep-alive>

Vue.prototype.$once

不知道这个事件是干嘛用的

代码语言:javascript
代码运行次数:0
复制
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
  }

Vue.prototype.$off

用于解绑事件监听

  • 判断参数是否存在,不存在直接返回
  • 判断事件参数是不是数组,逐个解绑事件
代码语言:javascript
代码运行次数:0
复制
  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
    if (Array.isArray(event)) {
      for (let i = 0, l = event.length; i < l; i++) {
        vm.$off(event[i], fn)
      }
      return vm
    }
    // specific event
    const cbs = vm._events[event]
    if (!cbs) {
      return vm
    }
    if (!fn) {
      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
  }

ue.prototype.$emit

事件派发

代码语言:javascript
代码运行次数:0
复制
 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)
      const info = `event handler for "${event}"`
      for (let i = 0, l = cbs.length; i < l; i++) {
        invokeWithErrorHandling(cbs[i], vm, args, vm, info)
      }
    }
    return vm
  }

从源码阅读过程中,我还发现 hook 可以这样写:

代码语言:javascript
代码运行次数:0
复制
beforeCreate: [
                    function a() { console.log("beforeCreate")},
                    function b() { console.log("beforeCreate")}
],
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CryptoCode 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • initEvents
  • add
  • remove
  • createOnceHandler
  • updateComponentListeners
  • eventsMixin
    • Vue.prototype.$on
    • Vue.prototype.$once
    • Vue.prototype.$off
    • ue.prototype.$emit
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档