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,
|
h,
|
||||||
nodeOps,
|
nodeOps,
|
||||||
toHandlers,
|
toHandlers,
|
||||||
nextTick
|
nextTick,
|
||||||
|
ComponentPublicInstance
|
||||||
} from '@vue/runtime-test'
|
} from '@vue/runtime-test'
|
||||||
import { isEmitListener } from '../src/componentEmits'
|
import { isEmitListener } from '../src/componentEmits'
|
||||||
|
|
||||||
|
@ -454,4 +455,55 @@ describe('component: emit', () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(fn).not.toHaveBeenCalled()
|
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))
|
return i.setupContext || (i.setupContext = createSetupContext(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePropsOrEmits(props: ComponentPropsOptions | EmitsOptions) {
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function normalizePropsOrEmits(
|
||||||
|
props: ComponentPropsOptions | EmitsOptions
|
||||||
|
) {
|
||||||
return isArray(props)
|
return isArray(props)
|
||||||
? props.reduce(
|
? props.reduce(
|
||||||
(normalized, p) => ((normalized[p] = {}), normalized),
|
(normalized, p) => ((normalized[p] = null), normalized),
|
||||||
{} as ComponentObjectPropsOptions | ObjectEmitsOptions
|
{} as ComponentObjectPropsOptions | ObjectEmitsOptions
|
||||||
)
|
)
|
||||||
: props
|
: props
|
||||||
|
|
|
@ -51,7 +51,8 @@ import {
|
||||||
import {
|
import {
|
||||||
ComponentObjectPropsOptions,
|
ComponentObjectPropsOptions,
|
||||||
ExtractPropTypes,
|
ExtractPropTypes,
|
||||||
ExtractDefaultPropTypes
|
ExtractDefaultPropTypes,
|
||||||
|
ComponentPropsOptions
|
||||||
} from './componentProps'
|
} from './componentProps'
|
||||||
import { EmitsOptions, EmitsToProps } from './componentEmits'
|
import { EmitsOptions, EmitsToProps } from './componentEmits'
|
||||||
import { Directive } from './directives'
|
import { Directive } from './directives'
|
||||||
|
@ -75,6 +76,7 @@ import {
|
||||||
import { OptionMergeFunction } from './apiCreateApp'
|
import { OptionMergeFunction } from './apiCreateApp'
|
||||||
import { LifecycleHooks } from './enums'
|
import { LifecycleHooks } from './enums'
|
||||||
import { SlotsType } from './componentSlots'
|
import { SlotsType } from './componentSlots'
|
||||||
|
import { normalizePropsOrEmits } from './apiSetupHelpers'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for declaring custom options.
|
* Interface for declaring custom options.
|
||||||
|
@ -1069,8 +1071,8 @@ export function mergeOptions(
|
||||||
|
|
||||||
export const internalOptionMergeStrats: Record<string, Function> = {
|
export const internalOptionMergeStrats: Record<string, Function> = {
|
||||||
data: mergeDataFn,
|
data: mergeDataFn,
|
||||||
props: mergeObjectOptions, // TODO
|
props: mergeEmitsOrPropsOptions,
|
||||||
emits: mergeObjectOptions, // TODO
|
emits: mergeEmitsOrPropsOptions,
|
||||||
// objects
|
// objects
|
||||||
methods: mergeObjectOptions,
|
methods: mergeObjectOptions,
|
||||||
computed: 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) {
|
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(
|
function mergeWatchOptions(
|
||||||
|
|
Loading…
Reference in New Issue