Skip to content

Commit c94ef02

Browse files
authored
fix(runtime-core): properly merge props and emits options from mixins (#8052)
close #7989
1 parent 2ce23f0 commit c94ef02

File tree

3 files changed

+92
-7
lines changed

3 files changed

+92
-7
lines changed

packages/runtime-core/__tests__/componentEmits.spec.ts

+53-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
h,
99
nodeOps,
1010
toHandlers,
11-
nextTick
11+
nextTick,
12+
ComponentPublicInstance
1213
} from '@vue/runtime-test'
1314
import { isEmitListener } from '../src/componentEmits'
1415

@@ -454,4 +455,55 @@ describe('component: emit', () => {
454455
await nextTick()
455456
expect(fn).not.toHaveBeenCalled()
456457
})
458+
459+
test('merge string array emits', async () => {
460+
const ComponentA = defineComponent({
461+
emits: ['one', 'two']
462+
})
463+
const ComponentB = defineComponent({
464+
emits: ['three']
465+
})
466+
const renderFn = vi.fn(function (this: ComponentPublicInstance) {
467+
expect(this.$options.emits).toEqual(['one', 'two', 'three'])
468+
return h('div')
469+
})
470+
const ComponentC = defineComponent({
471+
render: renderFn,
472+
mixins: [ComponentA, ComponentB]
473+
})
474+
const el = nodeOps.createElement('div')
475+
expect(renderFn).toHaveBeenCalledTimes(0)
476+
render(h(ComponentC), el)
477+
expect(renderFn).toHaveBeenCalledTimes(1)
478+
})
479+
480+
test('merge object emits', async () => {
481+
const twoFn = vi.fn((v: unknown) => !v)
482+
const ComponentA = defineComponent({
483+
emits: {
484+
one: null,
485+
two: twoFn
486+
}
487+
})
488+
const ComponentB = defineComponent({
489+
emits: ['three']
490+
})
491+
const renderFn = vi.fn(function (this: ComponentPublicInstance) {
492+
expect(this.$options.emits).toEqual({
493+
one: null,
494+
two: twoFn,
495+
three: null
496+
})
497+
expect(this.$options.emits.two).toBe(twoFn)
498+
return h('div')
499+
})
500+
const ComponentC = defineComponent({
501+
render: renderFn,
502+
mixins: [ComponentA, ComponentB]
503+
})
504+
const el = nodeOps.createElement('div')
505+
expect(renderFn).toHaveBeenCalledTimes(0)
506+
render(h(ComponentC), el)
507+
expect(renderFn).toHaveBeenCalledTimes(1)
508+
})
457509
})

packages/runtime-core/src/apiSetupHelpers.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -396,10 +396,15 @@ function getContext(): SetupContext {
396396
return i.setupContext || (i.setupContext = createSetupContext(i))
397397
}
398398

399-
function normalizePropsOrEmits(props: ComponentPropsOptions | EmitsOptions) {
399+
/**
400+
* @internal
401+
*/
402+
export function normalizePropsOrEmits(
403+
props: ComponentPropsOptions | EmitsOptions
404+
) {
400405
return isArray(props)
401406
? props.reduce(
402-
(normalized, p) => ((normalized[p] = {}), normalized),
407+
(normalized, p) => ((normalized[p] = null), normalized),
403408
{} as ComponentObjectPropsOptions | ObjectEmitsOptions
404409
)
405410
: props

packages/runtime-core/src/componentOptions.ts

+32-4
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ import {
5151
import {
5252
ComponentObjectPropsOptions,
5353
ExtractPropTypes,
54-
ExtractDefaultPropTypes
54+
ExtractDefaultPropTypes,
55+
ComponentPropsOptions
5556
} from './componentProps'
5657
import { EmitsOptions, EmitsToProps } from './componentEmits'
5758
import { Directive } from './directives'
@@ -75,6 +76,7 @@ import {
7576
import { OptionMergeFunction } from './apiCreateApp'
7677
import { LifecycleHooks } from './enums'
7778
import { SlotsType } from './componentSlots'
79+
import { normalizePropsOrEmits } from './apiSetupHelpers'
7880

7981
/**
8082
* Interface for declaring custom options.
@@ -1069,8 +1071,8 @@ export function mergeOptions(
10691071

10701072
export const internalOptionMergeStrats: Record<string, Function> = {
10711073
data: mergeDataFn,
1072-
props: mergeObjectOptions, // TODO
1073-
emits: mergeObjectOptions, // TODO
1074+
props: mergeEmitsOrPropsOptions,
1075+
emits: mergeEmitsOrPropsOptions,
10741076
// objects
10751077
methods: mergeObjectOptions,
10761078
computed: mergeObjectOptions,
@@ -1147,7 +1149,33 @@ function mergeAsArray<T = Function>(to: T[] | T | undefined, from: T | T[]) {
11471149
}
11481150

11491151
function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
1150-
return to ? extend(extend(Object.create(null), to), from) : from
1152+
return to ? extend(Object.create(null), to, from) : from
1153+
}
1154+
1155+
function mergeEmitsOrPropsOptions(
1156+
to: EmitsOptions | undefined,
1157+
from: EmitsOptions | undefined
1158+
): EmitsOptions | undefined
1159+
function mergeEmitsOrPropsOptions(
1160+
to: ComponentPropsOptions | undefined,
1161+
from: ComponentPropsOptions | undefined
1162+
): ComponentPropsOptions | undefined
1163+
function mergeEmitsOrPropsOptions(
1164+
to: ComponentPropsOptions | EmitsOptions | undefined,
1165+
from: ComponentPropsOptions | EmitsOptions | undefined
1166+
) {
1167+
if (to) {
1168+
if (isArray(to) && isArray(from)) {
1169+
return [...new Set([...to, ...from])]
1170+
}
1171+
return extend(
1172+
Object.create(null),
1173+
normalizePropsOrEmits(to),
1174+
normalizePropsOrEmits(from ?? {})
1175+
)
1176+
} else {
1177+
return from
1178+
}
11511179
}
11521180

11531181
function mergeWatchOptions(

0 commit comments

Comments
 (0)