fix(runtime-core): fix props/emits resolving with global mixins
fix #1975
This commit is contained in:
		
							parent
							
								
									2bbeea9a51
								
							
						
					
					
						commit
						8ed0b342d4
					
				| @ -178,40 +178,13 @@ describe('component: emit', () => { | ||||
|     expect(fn).toHaveBeenCalledTimes(1) | ||||
|   }) | ||||
| 
 | ||||
|   describe('isEmitListener', () => { | ||||
|     test('array option', () => { | ||||
|       const def1 = { emits: ['click'] } | ||||
|       expect(isEmitListener(def1, 'onClick')).toBe(true) | ||||
|       expect(isEmitListener(def1, 'onclick')).toBe(false) | ||||
|       expect(isEmitListener(def1, 'onBlick')).toBe(false) | ||||
|     }) | ||||
| 
 | ||||
|     test('object option', () => { | ||||
|       const def2 = { emits: { click: null } } | ||||
|       expect(isEmitListener(def2, 'onClick')).toBe(true) | ||||
|       expect(isEmitListener(def2, 'onclick')).toBe(false) | ||||
|       expect(isEmitListener(def2, 'onBlick')).toBe(false) | ||||
|     }) | ||||
| 
 | ||||
|     test('with mixins and extends', () => { | ||||
|       const mixin1 = { emits: ['foo'] } | ||||
|       const mixin2 = { emits: ['bar'] } | ||||
|       const extend = { emits: ['baz'] } | ||||
|       const def3 = { | ||||
|         mixins: [mixin1, mixin2], | ||||
|         extends: extend | ||||
|       } | ||||
|       expect(isEmitListener(def3, 'onFoo')).toBe(true) | ||||
|       expect(isEmitListener(def3, 'onBar')).toBe(true) | ||||
|       expect(isEmitListener(def3, 'onBaz')).toBe(true) | ||||
|       expect(isEmitListener(def3, 'onclick')).toBe(false) | ||||
|       expect(isEmitListener(def3, 'onBlick')).toBe(false) | ||||
|     }) | ||||
| 
 | ||||
|     test('.once listeners', () => { | ||||
|       const def2 = { emits: { click: null } } | ||||
|       expect(isEmitListener(def2, 'onClickOnce')).toBe(true) | ||||
|       expect(isEmitListener(def2, 'onclickOnce')).toBe(false) | ||||
|     }) | ||||
|   test('isEmitListener', () => { | ||||
|     const options = { click: null } | ||||
|     expect(isEmitListener(options, 'onClick')).toBe(true) | ||||
|     expect(isEmitListener(options, 'onclick')).toBe(false) | ||||
|     expect(isEmitListener(options, 'onBlick')).toBe(false) | ||||
|     // .once listeners
 | ||||
|     expect(isEmitListener(options, 'onClickOnce')).toBe(true) | ||||
|     expect(isEmitListener(options, 'onclickOnce')).toBe(false) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| @ -7,7 +7,8 @@ import { | ||||
|   FunctionalComponent, | ||||
|   defineComponent, | ||||
|   ref, | ||||
|   serializeInner | ||||
|   serializeInner, | ||||
|   createApp | ||||
| } from '@vue/runtime-test' | ||||
| import { render as domRender, nextTick } from 'vue' | ||||
| 
 | ||||
| @ -309,4 +310,44 @@ describe('component props', () => { | ||||
|     expect(setupProps).toMatchObject(props) | ||||
|     expect(renderProxy.$props).toMatchObject(props) | ||||
|   }) | ||||
| 
 | ||||
|   test('merging props from global mixins', () => { | ||||
|     let setupProps: any | ||||
|     let renderProxy: any | ||||
| 
 | ||||
|     const M1 = { | ||||
|       props: ['m1'] | ||||
|     } | ||||
|     const M2 = { | ||||
|       props: { m2: null } | ||||
|     } | ||||
|     const Comp = { | ||||
|       props: ['self'], | ||||
|       setup(props: any) { | ||||
|         setupProps = props | ||||
|       }, | ||||
|       render(this: any) { | ||||
|         renderProxy = this | ||||
|         return h('div', [this.self, this.m1, this.m2]) | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const props = { | ||||
|       self: 'from self, ', | ||||
|       m1: 'from mixin 1, ', | ||||
|       m2: 'from mixin 2' | ||||
|     } | ||||
|     const app = createApp(Comp, props) | ||||
|     app.mixin(M1) | ||||
|     app.mixin(M2) | ||||
| 
 | ||||
|     const root = nodeOps.createElement('div') | ||||
|     app.mount(root) | ||||
| 
 | ||||
|     expect(serializeInner(root)).toMatch( | ||||
|       `from self, from mixin 1, from mixin 2` | ||||
|     ) | ||||
|     expect(setupProps).toMatchObject(props) | ||||
|     expect(renderProxy.$props).toMatchObject(props) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| @ -33,6 +33,7 @@ export interface App<HostElement = any> { | ||||
|   provide<T>(key: InjectionKey<T> | string, value: T): this | ||||
| 
 | ||||
|   // internal, but we need to expose these for the server-renderer and devtools
 | ||||
|   _uid: number | ||||
|   _component: ConcreteComponent | ||||
|   _props: Data | null | ||||
|   _container: HostElement | null | ||||
| @ -108,6 +109,8 @@ export type CreateAppFunction<HostElement> = ( | ||||
|   rootProps?: Data | null | ||||
| ) => App<HostElement> | ||||
| 
 | ||||
| let uid = 0 | ||||
| 
 | ||||
| export function createAppAPI<HostElement>( | ||||
|   render: RootRenderFunction, | ||||
|   hydrate?: RootHydrateFunction | ||||
| @ -124,6 +127,7 @@ export function createAppAPI<HostElement>( | ||||
|     let isMounted = false | ||||
| 
 | ||||
|     const app: App = (context.app = { | ||||
|       _uid: uid++, | ||||
|       _component: rootComponent as ConcreteComponent, | ||||
|       _props: rootProps, | ||||
|       _container: null, | ||||
|  | ||||
| @ -18,7 +18,8 @@ import { | ||||
| import { | ||||
|   ComponentPropsOptions, | ||||
|   NormalizedPropsOptions, | ||||
|   initProps | ||||
|   initProps, | ||||
|   normalizePropsOptions | ||||
| } from './componentProps' | ||||
| import { Slots, initSlots, InternalSlots } from './componentSlots' | ||||
| import { warn } from './warning' | ||||
| @ -30,7 +31,8 @@ import { | ||||
|   EmitsOptions, | ||||
|   ObjectEmitsOptions, | ||||
|   EmitFn, | ||||
|   emit | ||||
|   emit, | ||||
|   normalizeEmitsOptions | ||||
| } from './componentEmits' | ||||
| import { | ||||
|   EMPTY_OBJ, | ||||
| @ -72,11 +74,11 @@ export interface ComponentInternalOptions { | ||||
|   /** | ||||
|    * @internal | ||||
|    */ | ||||
|   __props?: NormalizedPropsOptions | [] | ||||
|   __props?: Record<number, NormalizedPropsOptions> | ||||
|   /** | ||||
|    * @internal | ||||
|    */ | ||||
|   __emits?: ObjectEmitsOptions | ||||
|   __emits?: Record<number, ObjectEmitsOptions | null> | ||||
|   /** | ||||
|    * @internal | ||||
|    */ | ||||
| @ -231,6 +233,16 @@ export interface ComponentInternalInstance { | ||||
|    * @internal | ||||
|    */ | ||||
|   directives: Record<string, Directive> | null | ||||
|   /** | ||||
|    * reoslved props options | ||||
|    * @internal | ||||
|    */ | ||||
|   propsOptions: NormalizedPropsOptions | ||||
|   /** | ||||
|    * resolved emits options | ||||
|    * @internal | ||||
|    */ | ||||
|   emitsOptions: ObjectEmitsOptions | null | ||||
| 
 | ||||
|   // the rest are only for stateful components ---------------------------------
 | ||||
| 
 | ||||
| @ -254,14 +266,17 @@ export interface ComponentInternalInstance { | ||||
|    */ | ||||
|   ctx: Data | ||||
| 
 | ||||
|   // internal state
 | ||||
|   // state
 | ||||
|   data: Data | ||||
|   props: Data | ||||
|   attrs: Data | ||||
|   slots: InternalSlots | ||||
|   refs: Data | ||||
|   emit: EmitFn | ||||
|   // used for keeping track of .once event handlers on components
 | ||||
|   /** | ||||
|    * used for keeping track of .once event handlers on components | ||||
|    * @internal | ||||
|    */ | ||||
|   emitted: Record<string, boolean> | null | ||||
| 
 | ||||
|   /** | ||||
| @ -387,6 +402,14 @@ export function createComponentInstance( | ||||
|     components: null, | ||||
|     directives: null, | ||||
| 
 | ||||
|     // resolved props and emits options
 | ||||
|     propsOptions: normalizePropsOptions(type, appContext), | ||||
|     emitsOptions: normalizeEmitsOptions(type, appContext), | ||||
| 
 | ||||
|     // emit
 | ||||
|     emit: null as any, // to be set immediately
 | ||||
|     emitted: null, | ||||
| 
 | ||||
|     // state
 | ||||
|     ctx: EMPTY_OBJ, | ||||
|     data: EMPTY_OBJ, | ||||
| @ -419,9 +442,7 @@ export function createComponentInstance( | ||||
|     a: null, | ||||
|     rtg: null, | ||||
|     rtc: null, | ||||
|     ec: null, | ||||
|     emit: null as any, // to be set immediately
 | ||||
|     emitted: null | ||||
|     ec: null | ||||
|   } | ||||
|   if (__DEV__) { | ||||
|     instance.ctx = createRenderContext(instance) | ||||
|  | ||||
| @ -8,12 +8,16 @@ import { | ||||
|   isFunction, | ||||
|   extend | ||||
| } from '@vue/shared' | ||||
| import { ComponentInternalInstance, ConcreteComponent } from './component' | ||||
| import { | ||||
|   ComponentInternalInstance, | ||||
|   ComponentOptions, | ||||
|   ConcreteComponent | ||||
| } from './component' | ||||
| import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling' | ||||
| import { warn } from './warning' | ||||
| import { normalizePropsOptions } from './componentProps' | ||||
| import { UnionToIntersection } from './helpers/typeUtils' | ||||
| import { devtoolsComponentEmit } from './devtools' | ||||
| import { AppContext } from './apiCreateApp' | ||||
| 
 | ||||
| export type ObjectEmitsOptions = Record< | ||||
|   string, | ||||
| @ -44,10 +48,12 @@ export function emit( | ||||
|   const props = instance.vnode.props || EMPTY_OBJ | ||||
| 
 | ||||
|   if (__DEV__) { | ||||
|     const options = normalizeEmitsOptions(instance.type) | ||||
|     if (options) { | ||||
|       if (!(event in options)) { | ||||
|         const propsOptions = normalizePropsOptions(instance.type)[0] | ||||
|     const { | ||||
|       emitsOptions, | ||||
|       propsOptions: [propsOptions] | ||||
|     } = instance | ||||
|     if (emitsOptions) { | ||||
|       if (!(event in emitsOptions)) { | ||||
|         if (!propsOptions || !(`on` + capitalize(event) in propsOptions)) { | ||||
|           warn( | ||||
|             `Component emitted event "${event}" but it is neither declared in ` + | ||||
| @ -55,7 +61,7 @@ export function emit( | ||||
|           ) | ||||
|         } | ||||
|       } else { | ||||
|         const validator = options[event] | ||||
|         const validator = emitsOptions[event] | ||||
|         if (isFunction(validator)) { | ||||
|           const isValid = validator(...args) | ||||
|           if (!isValid) { | ||||
| @ -98,11 +104,16 @@ export function emit( | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function normalizeEmitsOptions( | ||||
|   comp: ConcreteComponent | ||||
| ): ObjectEmitsOptions | undefined { | ||||
|   if (hasOwn(comp, '__emits')) { | ||||
|     return comp.__emits | ||||
| export function normalizeEmitsOptions( | ||||
|   comp: ConcreteComponent, | ||||
|   appContext: AppContext, | ||||
|   asMixin = false | ||||
| ): ObjectEmitsOptions | null { | ||||
|   const appId = appContext.app ? appContext.app._uid : -1 | ||||
|   const cache = comp.__emits || (comp.__emits = {}) | ||||
|   const cached = cache[appId] | ||||
|   if (cached !== undefined) { | ||||
|     return cached | ||||
|   } | ||||
| 
 | ||||
|   const raw = comp.emits | ||||
| @ -111,18 +122,23 @@ function normalizeEmitsOptions( | ||||
|   // apply mixin/extends props
 | ||||
|   let hasExtends = false | ||||
|   if (__FEATURE_OPTIONS_API__ && !isFunction(comp)) { | ||||
|     if (comp.extends) { | ||||
|     const extendEmits = (raw: ComponentOptions) => { | ||||
|       hasExtends = true | ||||
|       extend(normalized, normalizeEmitsOptions(comp.extends)) | ||||
|       extend(normalized, normalizeEmitsOptions(raw, appContext, true)) | ||||
|     } | ||||
|     if (!asMixin && appContext.mixins.length) { | ||||
|       appContext.mixins.forEach(extendEmits) | ||||
|     } | ||||
|     if (comp.extends) { | ||||
|       extendEmits(comp.extends) | ||||
|     } | ||||
|     if (comp.mixins) { | ||||
|       hasExtends = true | ||||
|       comp.mixins.forEach(m => extend(normalized, normalizeEmitsOptions(m))) | ||||
|       comp.mixins.forEach(extendEmits) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (!raw && !hasExtends) { | ||||
|     return (comp.__emits = undefined) | ||||
|     return (cache[appId] = null) | ||||
|   } | ||||
| 
 | ||||
|   if (isArray(raw)) { | ||||
| @ -130,20 +146,22 @@ function normalizeEmitsOptions( | ||||
|   } else { | ||||
|     extend(normalized, raw) | ||||
|   } | ||||
|   return (comp.__emits = normalized) | ||||
|   return (cache[appId] = normalized) | ||||
| } | ||||
| 
 | ||||
| // Check if an incoming prop key is a declared emit event listener.
 | ||||
| // e.g. With `emits: { click: null }`, props named `onClick` and `onclick` are
 | ||||
| // both considered matched listeners.
 | ||||
| export function isEmitListener(comp: ConcreteComponent, key: string): boolean { | ||||
|   let emits: ObjectEmitsOptions | undefined | ||||
|   if (!isOn(key) || !(emits = normalizeEmitsOptions(comp))) { | ||||
| export function isEmitListener( | ||||
|   options: ObjectEmitsOptions | null, | ||||
|   key: string | ||||
| ): boolean { | ||||
|   if (!options || !isOn(key)) { | ||||
|     return false | ||||
|   } | ||||
|   key = key.replace(/Once$/, '') | ||||
|   return ( | ||||
|     hasOwn(emits, key[2].toLowerCase() + key.slice(3)) || | ||||
|     hasOwn(emits, key.slice(2)) | ||||
|     hasOwn(options, key[2].toLowerCase() + key.slice(3)) || | ||||
|     hasOwn(options, key.slice(2)) | ||||
|   ) | ||||
| } | ||||
|  | ||||
| @ -42,11 +42,7 @@ import { | ||||
|   WritableComputedOptions, | ||||
|   toRaw | ||||
| } from '@vue/reactivity' | ||||
| import { | ||||
|   ComponentObjectPropsOptions, | ||||
|   ExtractPropTypes, | ||||
|   normalizePropsOptions | ||||
| } from './componentProps' | ||||
| import { ComponentObjectPropsOptions, ExtractPropTypes } from './componentProps' | ||||
| import { EmitsOptions } from './componentEmits' | ||||
| import { Directive } from './directives' | ||||
| import { | ||||
| @ -431,7 +427,7 @@ export function applyOptions( | ||||
|   const checkDuplicateProperties = __DEV__ ? createDuplicateChecker() : null | ||||
| 
 | ||||
|   if (__DEV__) { | ||||
|     const propsOptions = normalizePropsOptions(options)[0] | ||||
|     const [propsOptions] = instance.propsOptions | ||||
|     if (propsOptions) { | ||||
|       for (const key in propsOptions) { | ||||
|         checkDuplicateProperties!(OptionTypes.PROPS, key) | ||||
|  | ||||
| @ -31,6 +31,7 @@ import { | ||||
| } from './component' | ||||
| import { isEmitListener } from './componentEmits' | ||||
| import { InternalObjectKey } from './vnode' | ||||
| import { AppContext } from './apiCreateApp' | ||||
| 
 | ||||
| export type ComponentPropsOptions<P = Data> = | ||||
|   | ComponentObjectPropsOptions<P> | ||||
| @ -107,7 +108,8 @@ type NormalizedProp = | ||||
| 
 | ||||
| // normalized value is a tuple of the actual normalized options
 | ||||
| // and an array of prop keys that need value casting (booleans and defaults)
 | ||||
| export type NormalizedPropsOptions = [Record<string, NormalizedProp>, string[]] | ||||
| export type NormalizedProps = Record<string, NormalizedProp> | ||||
| export type NormalizedPropsOptions = [NormalizedProps, string[]] | [] | ||||
| 
 | ||||
| export function initProps( | ||||
|   instance: ComponentInternalInstance, | ||||
| @ -121,7 +123,7 @@ export function initProps( | ||||
|   setFullProps(instance, rawProps, props, attrs) | ||||
|   // validation
 | ||||
|   if (__DEV__) { | ||||
|     validateProps(props, instance.type) | ||||
|     validateProps(props, instance) | ||||
|   } | ||||
| 
 | ||||
|   if (isStateful) { | ||||
| @ -151,7 +153,7 @@ export function updateProps( | ||||
|     vnode: { patchFlag } | ||||
|   } = instance | ||||
|   const rawCurrentProps = toRaw(props) | ||||
|   const [options] = normalizePropsOptions(instance.type) | ||||
|   const [options] = instance.propsOptions | ||||
| 
 | ||||
|   if ( | ||||
|     // always force full diff if hmr is enabled
 | ||||
| @ -236,7 +238,7 @@ export function updateProps( | ||||
|   trigger(instance, TriggerOpTypes.SET, '$attrs') | ||||
| 
 | ||||
|   if (__DEV__ && rawProps) { | ||||
|     validateProps(props, instance.type) | ||||
|     validateProps(props, instance) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -246,7 +248,7 @@ function setFullProps( | ||||
|   props: Data, | ||||
|   attrs: Data | ||||
| ) { | ||||
|   const [options, needCastKeys] = normalizePropsOptions(instance.type) | ||||
|   const [options, needCastKeys] = instance.propsOptions | ||||
|   if (rawProps) { | ||||
|     for (const key in rawProps) { | ||||
|       const value = rawProps[key] | ||||
| @ -259,7 +261,7 @@ function setFullProps( | ||||
|       let camelKey | ||||
|       if (options && hasOwn(options, (camelKey = camelize(key)))) { | ||||
|         props[camelKey] = value | ||||
|       } else if (!isEmitListener(instance.type, key)) { | ||||
|       } else if (!isEmitListener(instance.emitsOptions, key)) { | ||||
|         // Any non-declared (either as a prop or an emitted event) props are put
 | ||||
|         // into a separate `attrs` object for spreading. Make sure to preserve
 | ||||
|         // original key casing
 | ||||
| @ -283,7 +285,7 @@ function setFullProps( | ||||
| } | ||||
| 
 | ||||
| function resolvePropValue( | ||||
|   options: NormalizedPropsOptions[0], | ||||
|   options: NormalizedProps, | ||||
|   props: Data, | ||||
|   key: string, | ||||
|   value: unknown | ||||
| @ -315,10 +317,15 @@ function resolvePropValue( | ||||
| } | ||||
| 
 | ||||
| export function normalizePropsOptions( | ||||
|   comp: ConcreteComponent | ||||
| ): NormalizedPropsOptions | [] { | ||||
|   if (comp.__props) { | ||||
|     return comp.__props | ||||
|   comp: ConcreteComponent, | ||||
|   appContext: AppContext, | ||||
|   asMixin = false | ||||
| ): NormalizedPropsOptions { | ||||
|   const appId = appContext.app ? appContext.app._uid : -1 | ||||
|   const cache = comp.__props || (comp.__props = {}) | ||||
|   const cached = cache[appId] | ||||
|   if (cached) { | ||||
|     return cached | ||||
|   } | ||||
| 
 | ||||
|   const raw = comp.props | ||||
| @ -329,22 +336,24 @@ export function normalizePropsOptions( | ||||
|   let hasExtends = false | ||||
|   if (__FEATURE_OPTIONS_API__ && !isFunction(comp)) { | ||||
|     const extendProps = (raw: ComponentOptions) => { | ||||
|       const [props, keys] = normalizePropsOptions(raw) | ||||
|       hasExtends = true | ||||
|       const [props, keys] = normalizePropsOptions(raw, appContext, true) | ||||
|       extend(normalized, props) | ||||
|       if (keys) needCastKeys.push(...keys) | ||||
|     } | ||||
|     if (!asMixin && appContext.mixins.length) { | ||||
|       appContext.mixins.forEach(extendProps) | ||||
|     } | ||||
|     if (comp.extends) { | ||||
|       hasExtends = true | ||||
|       extendProps(comp.extends) | ||||
|     } | ||||
|     if (comp.mixins) { | ||||
|       hasExtends = true | ||||
|       comp.mixins.forEach(extendProps) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (!raw && !hasExtends) { | ||||
|     return (comp.__props = EMPTY_ARR) | ||||
|     return (cache[appId] = EMPTY_ARR) | ||||
|   } | ||||
| 
 | ||||
|   if (isArray(raw)) { | ||||
| @ -381,9 +390,8 @@ export function normalizePropsOptions( | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   const normalizedEntry: NormalizedPropsOptions = [normalized, needCastKeys] | ||||
|   comp.__props = normalizedEntry | ||||
|   return normalizedEntry | ||||
| 
 | ||||
|   return (cache[appId] = [normalized, needCastKeys]) | ||||
| } | ||||
| 
 | ||||
| // use function string name to check type constructors
 | ||||
| @ -416,9 +424,9 @@ function getTypeIndex( | ||||
| /** | ||||
|  * dev only | ||||
|  */ | ||||
| function validateProps(props: Data, comp: ConcreteComponent) { | ||||
| function validateProps(props: Data, instance: ComponentInternalInstance) { | ||||
|   const rawValues = toRaw(props) | ||||
|   const options = normalizePropsOptions(comp)[0] | ||||
|   const options = instance.propsOptions[0] | ||||
|   for (const key in options) { | ||||
|     let opt = options[key] | ||||
|     if (opt == null) continue | ||||
|  | ||||
| @ -29,7 +29,6 @@ import { | ||||
|   resolveMergedOptions, | ||||
|   isInBeforeCreate | ||||
| } from './componentOptions' | ||||
| import { normalizePropsOptions } from './componentProps' | ||||
| import { EmitsOptions, EmitFn } from './componentEmits' | ||||
| import { Slots } from './componentSlots' | ||||
| import { | ||||
| @ -250,7 +249,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = { | ||||
|       } else if ( | ||||
|         // only cache other properties when instance has declared (thus stable)
 | ||||
|         // props
 | ||||
|         (normalizedProps = normalizePropsOptions(type)[0]) && | ||||
|         (normalizedProps = instance.propsOptions[0]) && | ||||
|         hasOwn(normalizedProps, key) | ||||
|       ) { | ||||
|         accessCache![key] = AccessTypes.PROPS | ||||
| @ -354,7 +353,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = { | ||||
| 
 | ||||
|   has( | ||||
|     { | ||||
|       _: { data, setupState, accessCache, ctx, type, appContext } | ||||
|       _: { data, setupState, accessCache, ctx, appContext, propsOptions } | ||||
|     }: ComponentRenderContext, | ||||
|     key: string | ||||
|   ) { | ||||
| @ -363,8 +362,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = { | ||||
|       accessCache![key] !== undefined || | ||||
|       (data !== EMPTY_OBJ && hasOwn(data, key)) || | ||||
|       (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) || | ||||
|       ((normalizedProps = normalizePropsOptions(type)[0]) && | ||||
|         hasOwn(normalizedProps, key)) || | ||||
|       ((normalizedProps = propsOptions[0]) && hasOwn(normalizedProps, key)) || | ||||
|       hasOwn(ctx, key) || | ||||
|       hasOwn(publicPropertiesMap, key) || | ||||
|       hasOwn(appContext.config.globalProperties, key) | ||||
| @ -450,8 +448,10 @@ export function createRenderContext(instance: ComponentInternalInstance) { | ||||
| export function exposePropsOnRenderContext( | ||||
|   instance: ComponentInternalInstance | ||||
| ) { | ||||
|   const { ctx, type } = instance | ||||
|   const propsOptions = normalizePropsOptions(type)[0] | ||||
|   const { | ||||
|     ctx, | ||||
|     propsOptions: [propsOptions] | ||||
|   } = instance | ||||
|   if (propsOptions) { | ||||
|     Object.keys(propsOptions).forEach(key => { | ||||
|       Object.defineProperty(ctx, key, { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user