]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(runtime-core): remove emit return value
authorEvan You <yyx990803@gmail.com>
Fri, 10 Apr 2020 14:59:46 +0000 (10:59 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 10 Apr 2020 14:59:46 +0000 (10:59 -0400)
BREAKING CHANGE: this.$emit() and setupContext.emit() no longer
return values. For logic that relies on return value of listeners,
the listener should be declared as an `onXXX` prop and be called
directly. This still allows the parent component to pass in
a handler using `v-on`, since `v-on:foo` internally compiles
to `onFoo`.

    ref: https://github.com/vuejs/rfcs/pull/16

packages/runtime-core/__tests__/component.spec.ts [deleted file]
packages/runtime-core/__tests__/componentEmits.spec.ts
packages/runtime-core/__tests__/componentSlots.spec.ts [new file with mode: 0644]
packages/runtime-core/__tests__/errorHandling.spec.ts
packages/runtime-core/src/componentEmits.ts

diff --git a/packages/runtime-core/__tests__/component.spec.ts b/packages/runtime-core/__tests__/component.spec.ts
deleted file mode 100644 (file)
index dad6f8c..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-import {
-  h,
-  ref,
-  render,
-  nodeOps,
-  nextTick,
-  defineComponent
-} from '@vue/runtime-test'
-
-describe('renderer: component', () => {
-  test.todo('should work')
-
-  test.todo('shouldUpdateComponent')
-
-  test.todo('componentProxy')
-
-  describe('componentProps', () => {
-    test.todo('should work')
-
-    test('should convert empty booleans to true', () => {
-      let b1: any, b2: any, b3: any
-
-      const Comp = defineComponent({
-        props: {
-          b1: Boolean,
-          b2: [Boolean, String],
-          b3: [String, Boolean]
-        },
-        setup(props) {
-          ;({ b1, b2, b3 } = props)
-          return () => ''
-        }
-      })
-
-      render(
-        h(Comp, <any>{ b1: '', b2: '', b3: '' }),
-        nodeOps.createElement('div')
-      )
-
-      expect(b1).toBe(true)
-      expect(b2).toBe(true)
-      expect(b3).toBe('')
-    })
-  })
-
-  describe('slots', () => {
-    test('should respect $stable flag', async () => {
-      const flag1 = ref(1)
-      const flag2 = ref(2)
-      const spy = jest.fn()
-
-      const Child = () => {
-        spy()
-        return 'child'
-      }
-
-      const App = {
-        setup() {
-          return () => [
-            flag1.value,
-            h(
-              Child,
-              { n: flag2.value },
-              {
-                foo: () => 'foo',
-                $stable: true
-              }
-            )
-          ]
-        }
-      }
-
-      render(h(App), nodeOps.createElement('div'))
-      expect(spy).toHaveBeenCalledTimes(1)
-
-      // parent re-render, props didn't change, slots are stable
-      // -> child should not update
-      flag1.value++
-      await nextTick()
-      expect(spy).toHaveBeenCalledTimes(1)
-
-      // parent re-render, props changed
-      // -> child should update
-      flag2.value++
-      await nextTick()
-      expect(spy).toHaveBeenCalledTimes(2)
-    })
-  })
-
-  test('emit', async () => {
-    let noMatchEmitResult: any
-    let singleEmitResult: any
-    let multiEmitResult: any
-
-    const Child = defineComponent({
-      setup(_, { emit }) {
-        noMatchEmitResult = emit('foo')
-        singleEmitResult = emit('bar')
-        multiEmitResult = emit('baz')
-        return () => h('div')
-      }
-    })
-
-    const App = {
-      setup() {
-        return () =>
-          h(Child, {
-            // emit triggering single handler
-            onBar: () => 1,
-            // emit triggering multiple handlers
-            onBaz: [() => Promise.resolve(2), () => Promise.resolve(3)]
-          })
-      }
-    }
-
-    render(h(App), nodeOps.createElement('div'))
-
-    // assert return values from emit
-    expect(noMatchEmitResult).toMatchObject([])
-    expect(singleEmitResult).toMatchObject([1])
-    expect(await Promise.all(multiEmitResult)).toMatchObject([2, 3])
-  })
-
-  // for v-model:foo-bar usage in DOM templates
-  test('emit update:xxx events should trigger kebab-case equivalent', () => {
-    const Child = defineComponent({
-      setup(_, { emit }) {
-        emit('update:fooBar', 1)
-        return () => h('div')
-      }
-    })
-
-    const handler = jest.fn()
-    const App = {
-      setup() {
-        return () =>
-          h(Child, {
-            'onUpdate:foo-bar': handler
-          })
-      }
-    }
-
-    render(h(App), nodeOps.createElement('div'))
-    expect(handler).toHaveBeenCalled()
-  })
-})
index 331b814cd3ce173e8864abd1763bc3c96d9a9fed..2807366bcfed3cc4e3a4dfad50a4089f0087b8f0 100644 (file)
@@ -5,7 +5,7 @@ import { mockWarn } from '@vue/shared'
 import { render, defineComponent, h, nodeOps } from '@vue/runtime-test'
 import { isEmitListener } from '../src/componentEmits'
 
-describe('emits option', () => {
+describe('component: emit', () => {
   mockWarn()
 
   test('trigger both raw event and capitalize handlers', () => {
@@ -27,6 +27,7 @@ describe('emits option', () => {
     expect(onBar).toHaveBeenCalled()
   })
 
+  // for v-model:foo-bar usage in DOM templates
   test('trigger hyphendated events for update:xxx events', () => {
     const Foo = defineComponent({
       render() {},
@@ -49,6 +50,33 @@ describe('emits option', () => {
     expect(barSpy).toHaveBeenCalled()
   })
 
+  test('should trigger array of listeners', async () => {
+    const Child = defineComponent({
+      setup(_, { emit }) {
+        emit('foo', 1)
+        return () => h('div')
+      }
+    })
+
+    const fn1 = jest.fn()
+    const fn2 = jest.fn()
+
+    const App = {
+      setup() {
+        return () =>
+          h(Child, {
+            onFoo: [fn1, fn2]
+          })
+      }
+    }
+
+    render(h(App), nodeOps.createElement('div'))
+    expect(fn1).toHaveBeenCalledTimes(1)
+    expect(fn1).toHaveBeenCalledWith(1)
+    expect(fn2).toHaveBeenCalledTimes(1)
+    expect(fn1).toHaveBeenCalledWith(1)
+  })
+
   test('warning for undeclared event (array)', () => {
     const Foo = defineComponent({
       emits: ['foo'],
diff --git a/packages/runtime-core/__tests__/componentSlots.spec.ts b/packages/runtime-core/__tests__/componentSlots.spec.ts
new file mode 100644 (file)
index 0000000..ee85682
--- /dev/null
@@ -0,0 +1,47 @@
+import { ref, render, h, nodeOps, nextTick } from '@vue/runtime-test'
+
+describe('component: slots', () => {
+  // TODO more tests for slots normalization etc.
+
+  test('should respect $stable flag', async () => {
+    const flag1 = ref(1)
+    const flag2 = ref(2)
+    const spy = jest.fn()
+
+    const Child = () => {
+      spy()
+      return 'child'
+    }
+
+    const App = {
+      setup() {
+        return () => [
+          flag1.value,
+          h(
+            Child,
+            { n: flag2.value },
+            {
+              foo: () => 'foo',
+              $stable: true
+            }
+          )
+        ]
+      }
+    }
+
+    render(h(App), nodeOps.createElement('div'))
+    expect(spy).toHaveBeenCalledTimes(1)
+
+    // parent re-render, props didn't change, slots are stable
+    // -> child should not update
+    flag1.value++
+    await nextTick()
+    expect(spy).toHaveBeenCalledTimes(1)
+
+    // parent re-render, props changed
+    // -> child should update
+    flag2.value++
+    await nextTick()
+    expect(spy).toHaveBeenCalledTimes(2)
+  })
+})
index f6907949b1f67cbb28462780fe43d71395c425b2..4830b65eff5d89f8830cda2fc8437919cd5a655e 100644 (file)
@@ -416,21 +416,16 @@ describe('error handling', () => {
       }
     }
 
-    let res: any
     const Child = {
+      props: ['onFoo'],
       setup(props: any, { emit }: any) {
-        res = emit('foo')
+        emit('foo')
         return () => null
       }
     }
 
     render(h(Comp), nodeOps.createElement('div'))
-
-    try {
-      await Promise.all(res)
-    } catch (e) {
-      expect(e).toBe(err)
-    }
+    await nextTick()
     expect(fn).toHaveBeenCalledWith(err, 'component event handler')
   })
 
@@ -438,6 +433,12 @@ describe('error handling', () => {
     const err = new Error('foo')
     const fn = jest.fn()
 
+    const res: Promise<any>[] = []
+    const createAsyncHandler = (p: Promise<any>) => () => {
+      res.push(p)
+      return p
+    }
+
     const Comp = {
       setup() {
         onErrorCaptured((err, instance, info) => {
@@ -446,15 +447,17 @@ describe('error handling', () => {
         })
         return () =>
           h(Child, {
-            onFoo: [() => Promise.reject(err), () => Promise.resolve(1)]
+            onFoo: [
+              createAsyncHandler(Promise.reject(err)),
+              createAsyncHandler(Promise.resolve(1))
+            ]
           })
       }
     }
 
-    let res: any
     const Child = {
       setup(props: any, { emit }: any) {
-        res = emit('foo')
+        emit('foo')
         return () => null
       }
     }
index 7ece305c04f5a479f56e1ec423a03a6f45d24253..0cd348dd5ce9128e25271107ae0ce0d8b94d1720 100644 (file)
@@ -28,12 +28,12 @@ export type EmitFn<
   Options = ObjectEmitsOptions,
   Event extends keyof Options = keyof Options
 > = Options extends any[]
-  ? (event: Options[0], ...args: any[]) => unknown[]
+  ? (event: Options[0], ...args: any[]) => void
   : UnionToIntersection<
       {
         [key in Event]: Options[key] extends ((...args: infer Args) => any)
-          ? (event: key, ...args: Args) => unknown[]
-          : (event: key, ...args: any[]) => unknown[]
+          ? (event: key, ...args: Args) => void
+          : (event: key, ...args: any[]) => void
       }[Event]
     >
 
@@ -41,7 +41,7 @@ export function emit(
   instance: ComponentInternalInstance,
   event: string,
   ...args: any[]
-): any[] {
+) {
   const props = instance.vnode.props || EMPTY_OBJ
 
   if (__DEV__) {
@@ -74,15 +74,12 @@ export function emit(
     handler = props[`on${event}`] || props[`on${capitalize(event)}`]
   }
   if (handler) {
-    const res = callWithAsyncErrorHandling(
+    callWithAsyncErrorHandling(
       handler,
       instance,
       ErrorCodes.COMPONENT_EVENT_HANDLER,
       args
     )
-    return isArray(res) ? res : [res]
-  } else {
-    return []
   }
 }