]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: split EventEmitter and make it optional
authorEvan You <yyx990803@gmail.com>
Wed, 17 Oct 2018 00:32:18 +0000 (20:32 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 17 Oct 2018 00:32:18 +0000 (20:32 -0400)
packages/core/src/component.ts
packages/core/src/index.ts
packages/core/src/optional/directives.ts [moved from packages/core/src/optional/directive.ts with 100% similarity]
packages/core/src/optional/events.ts [new file with mode: 0644]
packages/core/src/optional/mixins.ts [moved from packages/core/src/optional/mixin.ts with 100% similarity]

index dbe4d8e457f4b1f39509c5df6b579fcbdb0a1e22..604ceeace83e916b6b9c1c38a12df0007e86efc8 100644 (file)
@@ -1,4 +1,4 @@
-import { EMPTY_OBJ, NOOP, isArray } from '@vue/shared'
+import { EMPTY_OBJ, NOOP } from '@vue/shared'
 import { VNode, Slots, RenderNode, MountedVNode } from './vdom'
 import {
   Data,
@@ -12,6 +12,7 @@ import { Autorun, DebuggerEvent, ComputedGetter } from '@vue/observer'
 import { nextTick } from '@vue/scheduler'
 import { ErrorTypes } from './errorHandling'
 import { initializeComponentInstance } from './componentUtils'
+import { EventEmitter, invokeListeners } from './optional/events'
 
 // public component instance type
 export interface Component<P = {}, D = {}> extends PublicInstanceMethods {
@@ -38,10 +39,7 @@ interface PublicInstanceMethods {
     cb: (this: this, newValue: any, oldValue: any) => void,
     options?: WatchOptions
   ): () => void
-  $on(event: string, fn: Function): this
-  $once(event: string, fn: Function): this
-  $off(event?: string, fn?: Function): this
-  $emit(name: string, ...payload: any[]): this
+  $emit(name: string, ...payload: any[]): void
 }
 
 export interface APIMethods<P = {}, D = {}> {
@@ -147,6 +145,9 @@ class InternalComponent implements PublicInstanceMethods {
       // access $props.
       this.$props = props
     }
+    if (__COMPAT__) {
+      ;(this as any)._eventEmitter = new EventEmitter(this)
+    }
   }
 
   // to be set by renderer during mount
@@ -164,55 +165,7 @@ class InternalComponent implements PublicInstanceMethods {
     return setupWatcher(this as any, keyOrFn, cb, options)
   }
 
-  // eventEmitter interface
-  $on(event: string, fn: Function): this {
-    if (isArray(event)) {
-      for (let i = 0; i < event.length; i++) {
-        this.$on(event[i], fn)
-      }
-    } else {
-      const events = this._events || (this._events = Object.create(null))
-      ;(events[event] || (events[event] = [])).push(fn)
-    }
-    return this
-  }
-
-  $once(event: string, fn: Function): this {
-    const onceFn = (...args: any[]) => {
-      this.$off(event, onceFn)
-      fn.apply(this, args)
-    }
-    ;(onceFn as any).fn = fn
-    return this.$on(event, onceFn)
-  }
-
-  $off(event?: string, fn?: Function): this {
-    if (this._events) {
-      if (!event && !fn) {
-        this._events = null
-      } else if (isArray(event)) {
-        for (let i = 0; i < event.length; i++) {
-          this.$off(event[i], fn)
-        }
-      } else if (!fn) {
-        this._events[event as string] = null
-      } else {
-        const fns = this._events[event as string]
-        if (fns) {
-          for (let i = 0; i < fns.length; i++) {
-            const f = fns[i]
-            if (fn === f || fn === (f as any).fn) {
-              fns.splice(i, 1)
-              break
-            }
-          }
-        }
-      }
-    }
-    return this
-  }
-
-  $emit(name: string, ...payload: any[]): this {
+  $emit(name: string, ...payload: any[]) {
     const parentData =
       (this.$parentVNode && this.$parentVNode.data) || EMPTY_OBJ
     const parentListener =
@@ -220,24 +173,23 @@ class InternalComponent implements PublicInstanceMethods {
     if (parentListener) {
       invokeListeners(parentListener, payload)
     }
-    if (this._events) {
-      const handlers = this._events[name]
-      if (handlers) {
-        invokeListeners(handlers, payload)
-      }
-    }
-    return this
   }
 }
 
-function invokeListeners(value: Function | Function[], payload: any[]) {
-  // TODO handle error
-  if (isArray(value)) {
-    for (let i = 0; i < value.length; i++) {
-      value[i](...payload)
+// legacy event emitter interface exposed on component instances
+if (__COMPAT__) {
+  const p = InternalComponent.prototype as any
+  ;['on', 'off', 'once'].forEach(key => {
+    p['$' + key] = function(...args: any[]) {
+      this._eventEmitter[key](...args)
+      return this
     }
-  } else {
-    value(...payload)
+  })
+  const emit = p.$emit
+  p.$emit = function(...args: any[]) {
+    emit.call(this, ...args)
+    this._eventEmitter.emit(...args)
+    return this
   }
 }
 
index 14b544de9d21f2c930b9bfe3722871bcfb5795b5..a4aeadeadd30d14afa34a8fcd5cf39dc55e6253c 100644 (file)
@@ -12,11 +12,12 @@ export { nextTick } from '@vue/scheduler'
 
 // Optional APIs
 // these are imported on-demand and can be tree-shaken
-export { applyDirectives } from './optional/directive'
+export { applyDirectives } from './optional/directives'
 export { Provide, Inject } from './optional/context'
 export { createAsyncComponent } from './optional/asyncComponent'
 export { KeepAlive } from './optional/keepAlive'
-export { mixins } from './optional/mixin'
+export { mixins } from './optional/mixins'
+export { EventEmitter } from './optional/events'
 
 // flags & types
 export { ComponentType, ComponentClass, FunctionalComponent } from './component'
diff --git a/packages/core/src/optional/events.ts b/packages/core/src/optional/events.ts
new file mode 100644 (file)
index 0000000..2fba5fc
--- /dev/null
@@ -0,0 +1,76 @@
+import { isArray } from '@vue/shared'
+
+export class EventEmitter {
+  ctx: any
+  events: { [event: string]: Function[] | null } = {}
+
+  constructor(ctx: any) {
+    this.ctx = ctx
+  }
+
+  // eventEmitter interface
+  on(event: string, fn: Function) {
+    if (isArray(event)) {
+      for (let i = 0; i < event.length; i++) {
+        this.on(event[i], fn)
+      }
+    } else {
+      const { events } = this
+      ;(events[event] || (events[event] = [])).push(fn)
+    }
+  }
+
+  once(event: string, fn: Function) {
+    const onceFn = (...args: any[]) => {
+      this.off(event, onceFn)
+      fn.apply(this, args)
+    }
+    ;(onceFn as any).fn = fn
+    this.on(event, onceFn)
+  }
+
+  off(event?: string, fn?: Function) {
+    if (!event && !fn) {
+      this.events = {}
+    } else if (isArray(event)) {
+      for (let i = 0; i < event.length; i++) {
+        this.off(event[i], fn)
+      }
+    } else if (!fn) {
+      this.events[event as string] = null
+    } else {
+      const fns = this.events[event as string]
+      if (fns) {
+        for (let i = 0; i < fns.length; i++) {
+          const f = fns[i]
+          if (fn === f || fn === (f as any).fn) {
+            fns.splice(i, 1)
+            break
+          }
+        }
+      }
+    }
+  }
+
+  emit(name: string, ...payload: any[]) {
+    const handlers = this.events[name]
+    if (handlers) {
+      invokeListeners(handlers, payload, this.ctx)
+    }
+  }
+}
+
+export function invokeListeners(
+  value: Function | Function[],
+  payload: any[],
+  ctx: any = null
+) {
+  // TODO handle error
+  if (isArray(value)) {
+    for (let i = 0; i < value.length; i++) {
+      value[i].call(ctx, ...payload)
+    }
+  } else {
+    value.call(ctx, ...payload)
+  }
+}