mirror of https://github.com/vuejs/core.git
fix(runtime-core): properly merge props and emits options from mixins (#8052)
close #7989
This commit is contained in:
parent
2ce23f0e09
commit
c94ef02421
|
@ -8,7 +8,8 @@ import {
|
|||
h,
|
||||
nodeOps,
|
||||
toHandlers,
|
||||
nextTick
|
||||
nextTick,
|
||||
ComponentPublicInstance
|
||||
} from '@vue/runtime-test'
|
||||
import { isEmitListener } from '../src/componentEmits'
|
||||
|
||||
|
@ -454,4 +455,55 @@ describe('component: emit', () => {
|
|||
await nextTick()
|
||||
expect(fn).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('merge string array emits', async () => {
|
||||
const ComponentA = defineComponent({
|
||||
emits: ['one', 'two']
|
||||
})
|
||||
const ComponentB = defineComponent({
|
||||
emits: ['three']
|
||||
})
|
||||
const renderFn = vi.fn(function (this: ComponentPublicInstance) {
|
||||
expect(this.$options.emits).toEqual(['one', 'two', 'three'])
|
||||
return h('div')
|
||||
})
|
||||
const ComponentC = defineComponent({
|
||||
render: renderFn,
|
||||
mixins: [ComponentA, ComponentB]
|
||||
})
|
||||
const el = nodeOps.createElement('div')
|
||||
expect(renderFn).toHaveBeenCalledTimes(0)
|
||||
render(h(ComponentC), el)
|
||||
expect(renderFn).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
test('merge object emits', async () => {
|
||||
const twoFn = vi.fn((v: unknown) => !v)
|
||||
const ComponentA = defineComponent({
|
||||
emits: {
|
||||
one: null,
|
||||
two: twoFn
|
||||
}
|
||||
})
|
||||
const ComponentB = defineComponent({
|
||||
emits: ['three']
|
||||
})
|
||||
const renderFn = vi.fn(function (this: ComponentPublicInstance) {
|
||||
expect(this.$options.emits).toEqual({
|
||||
one: null,
|
||||
two: twoFn,
|
||||
three: null
|
||||
})
|
||||
expect(this.$options.emits.two).toBe(twoFn)
|
||||
return h('div')
|
||||
})
|
||||
const ComponentC = defineComponent({
|
||||
render: renderFn,
|
||||
mixins: [ComponentA, ComponentB]
|
||||
})
|
||||
const el = nodeOps.createElement('div')
|
||||
expect(renderFn).toHaveBeenCalledTimes(0)
|
||||
render(h(ComponentC), el)
|
||||
expect(renderFn).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -396,10 +396,15 @@ function getContext(): SetupContext {
|
|||
return i.setupContext || (i.setupContext = createSetupContext(i))
|
||||
}
|
||||
|
||||
function normalizePropsOrEmits(props: ComponentPropsOptions | EmitsOptions) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function normalizePropsOrEmits(
|
||||
props: ComponentPropsOptions | EmitsOptions
|
||||
) {
|
||||
return isArray(props)
|
||||
? props.reduce(
|
||||
(normalized, p) => ((normalized[p] = {}), normalized),
|
||||
(normalized, p) => ((normalized[p] = null), normalized),
|
||||
{} as ComponentObjectPropsOptions | ObjectEmitsOptions
|
||||
)
|
||||
: props
|
||||
|
|
|
@ -51,7 +51,8 @@ import {
|
|||
import {
|
||||
ComponentObjectPropsOptions,
|
||||
ExtractPropTypes,
|
||||
ExtractDefaultPropTypes
|
||||
ExtractDefaultPropTypes,
|
||||
ComponentPropsOptions
|
||||
} from './componentProps'
|
||||
import { EmitsOptions, EmitsToProps } from './componentEmits'
|
||||
import { Directive } from './directives'
|
||||
|
@ -75,6 +76,7 @@ import {
|
|||
import { OptionMergeFunction } from './apiCreateApp'
|
||||
import { LifecycleHooks } from './enums'
|
||||
import { SlotsType } from './componentSlots'
|
||||
import { normalizePropsOrEmits } from './apiSetupHelpers'
|
||||
|
||||
/**
|
||||
* Interface for declaring custom options.
|
||||
|
@ -1069,8 +1071,8 @@ export function mergeOptions(
|
|||
|
||||
export const internalOptionMergeStrats: Record<string, Function> = {
|
||||
data: mergeDataFn,
|
||||
props: mergeObjectOptions, // TODO
|
||||
emits: mergeObjectOptions, // TODO
|
||||
props: mergeEmitsOrPropsOptions,
|
||||
emits: mergeEmitsOrPropsOptions,
|
||||
// objects
|
||||
methods: mergeObjectOptions,
|
||||
computed: mergeObjectOptions,
|
||||
|
@ -1147,7 +1149,33 @@ function mergeAsArray<T = Function>(to: T[] | T | undefined, from: T | T[]) {
|
|||
}
|
||||
|
||||
function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {
|
||||
return to ? extend(extend(Object.create(null), to), from) : from
|
||||
return to ? extend(Object.create(null), to, from) : from
|
||||
}
|
||||
|
||||
function mergeEmitsOrPropsOptions(
|
||||
to: EmitsOptions | undefined,
|
||||
from: EmitsOptions | undefined
|
||||
): EmitsOptions | undefined
|
||||
function mergeEmitsOrPropsOptions(
|
||||
to: ComponentPropsOptions | undefined,
|
||||
from: ComponentPropsOptions | undefined
|
||||
): ComponentPropsOptions | undefined
|
||||
function mergeEmitsOrPropsOptions(
|
||||
to: ComponentPropsOptions | EmitsOptions | undefined,
|
||||
from: ComponentPropsOptions | EmitsOptions | undefined
|
||||
) {
|
||||
if (to) {
|
||||
if (isArray(to) && isArray(from)) {
|
||||
return [...new Set([...to, ...from])]
|
||||
}
|
||||
return extend(
|
||||
Object.create(null),
|
||||
normalizePropsOrEmits(to),
|
||||
normalizePropsOrEmits(from ?? {})
|
||||
)
|
||||
} else {
|
||||
return from
|
||||
}
|
||||
}
|
||||
|
||||
function mergeWatchOptions(
|
||||
|
|
Loading…
Reference in New Issue