123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- /* @flow */
- import {
- tip,
- toArray,
- hyphenate,
- formatComponentName,
- invokeWithErrorHandling
- } from '../util/index'
- import { updateListeners } from '../vdom/helpers/index'
- 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)
- }
- }
- let target: any
- function add (event, fn) {
- target.$on(event, fn)
- }
- function remove (event, fn) {
- target.$off(event, fn)
- }
- function createOnceHandler (event, fn) {
- const _target = target
- return function onceHandler () {
- const res = fn.apply(null, arguments)
- if (res !== null) {
- _target.$off(event, onceHandler)
- }
- }
- }
- export function updateComponentListeners (
- vm: Component,
- listeners: Object,
- oldListeners: ?Object
- ) {
- target = vm
- updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)
- target = undefined
- }
- export function eventsMixin (Vue: Class<Component>) {
- 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++) {
- 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
- }
- 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 = 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
- }
- 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
- }
- }
|