1 |
|
2 |
|
3 | import {
|
4 | tip,
|
5 | toArray,
|
6 | hyphenate,
|
7 | formatComponentName,
|
8 | invokeWithErrorHandling
|
9 | } from '../util/index'
|
10 | import { updateListeners } from '../vdom/helpers/index'
|
11 |
|
12 | export function initEvents (vm: Component) {
|
13 | vm._events = Object.create(null)
|
14 | vm._hasHookEvent = false
|
15 |
|
16 | const listeners = vm.$options._parentListeners
|
17 | if (listeners) {
|
18 | updateComponentListeners(vm, listeners)
|
19 | }
|
20 | }
|
21 |
|
22 | let target: any
|
23 |
|
24 | function add (event, fn) {
|
25 | target.$on(event, fn)
|
26 | }
|
27 |
|
28 | function remove (event, fn) {
|
29 | target.$off(event, fn)
|
30 | }
|
31 |
|
32 | function createOnceHandler (event, fn) {
|
33 | const _target = target
|
34 | return function onceHandler () {
|
35 | const res = fn.apply(null, arguments)
|
36 | if (res !== null) {
|
37 | _target.$off(event, onceHandler)
|
38 | }
|
39 | }
|
40 | }
|
41 |
|
42 | export function updateComponentListeners (
|
43 | vm: Component,
|
44 | listeners: Object,
|
45 | oldListeners: ?Object
|
46 | ) {
|
47 | target = vm
|
48 | updateListeners(listeners, oldListeners || {}, add, remove, createOnceHandler, vm)
|
49 | target = undefined
|
50 | }
|
51 |
|
52 | export function eventsMixin (Vue: Class<Component>) {
|
53 | const hookRE = /^hook:/
|
54 | Vue.prototype.$on = function (event: string | Array<string>, fn: Function): Component {
|
55 | const vm: Component = this
|
56 | if (Array.isArray(event)) {
|
57 | for (let i = 0, l = event.length; i < l; i++) {
|
58 | vm.$on(event[i], fn)
|
59 | }
|
60 | } else {
|
61 | (vm._events[event] || (vm._events[event] = [])).push(fn)
|
62 |
|
63 |
|
64 | if (hookRE.test(event)) {
|
65 | vm._hasHookEvent = true
|
66 | }
|
67 | }
|
68 | return vm
|
69 | }
|
70 |
|
71 | Vue.prototype.$once = function (event: string, fn: Function): Component {
|
72 | const vm: Component = this
|
73 | function on () {
|
74 | vm.$off(event, on)
|
75 | fn.apply(vm, arguments)
|
76 | }
|
77 | on.fn = fn
|
78 | vm.$on(event, on)
|
79 | return vm
|
80 | }
|
81 |
|
82 | Vue.prototype.$off = function (event?: string | Array<string>, fn?: Function): Component {
|
83 | const vm: Component = this
|
84 |
|
85 | if (!arguments.length) {
|
86 | vm._events = Object.create(null)
|
87 | return vm
|
88 | }
|
89 |
|
90 | if (Array.isArray(event)) {
|
91 | for (let i = 0, l = event.length; i < l; i++) {
|
92 | vm.$off(event[i], fn)
|
93 | }
|
94 | return vm
|
95 | }
|
96 |
|
97 | const cbs = vm._events[event]
|
98 | if (!cbs) {
|
99 | return vm
|
100 | }
|
101 | if (!fn) {
|
102 | vm._events[event] = null
|
103 | return vm
|
104 | }
|
105 |
|
106 | let cb
|
107 | let i = cbs.length
|
108 | while (i--) {
|
109 | cb = cbs[i]
|
110 | if (cb === fn || cb.fn === fn) {
|
111 | cbs.splice(i, 1)
|
112 | break
|
113 | }
|
114 | }
|
115 | return vm
|
116 | }
|
117 |
|
118 | Vue.prototype.$emit = function (event: string): Component {
|
119 | const vm: Component = this
|
120 | if (process.env.NODE_ENV !== 'production') {
|
121 | const lowerCaseEvent = event.toLowerCase()
|
122 | if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
|
123 | tip(
|
124 | `Event "${lowerCaseEvent}" is emitted in component ` +
|
125 | `${formatComponentName(vm)} but the handler is registered for "${event}". ` +
|
126 | `Note that HTML attributes are case-insensitive and you cannot use ` +
|
127 | `v-on to listen to camelCase events when using in-DOM templates. ` +
|
128 | `You should probably use "${hyphenate(event)}" instead of "${event}".`
|
129 | )
|
130 | }
|
131 | }
|
132 | let cbs = vm._events[event]
|
133 | if (cbs) {
|
134 | cbs = cbs.length > 1 ? toArray(cbs) : cbs
|
135 | const args = toArray(arguments, 1)
|
136 | const info = `event handler for "${event}"`
|
137 | for (let i = 0, l = cbs.length; i < l; i++) {
|
138 | invokeWithErrorHandling(cbs[i], vm, args, vm, info)
|
139 | }
|
140 | }
|
141 | return vm
|
142 | }
|
143 | }
|