wip: compat for legacy functional component
This commit is contained in:
		
							parent
							
								
									d71c488540
								
							
						
					
					
						commit
						457a56e331
					
				| @ -1,16 +1,25 @@ | ||||
| import { isArray, isFunction, isObject, isPromise } from '@vue/shared' | ||||
| import { defineAsyncComponent } from '../apiAsyncComponent' | ||||
| import { Component, ComponentOptions, FunctionalComponent } from '../component' | ||||
| import { | ||||
|   Component, | ||||
|   ComponentOptions, | ||||
|   FunctionalComponent, | ||||
|   getCurrentInstance | ||||
| } from '../component' | ||||
| import { resolveInjections } from '../componentOptions' | ||||
| import { InternalSlots } from '../componentSlots' | ||||
| import { isVNode } from '../vnode' | ||||
| import { softAssertCompatEnabled } from './compatConfig' | ||||
| import { DeprecationTypes } from './deprecations' | ||||
| import { isCompatEnabled, softAssertCompatEnabled } from './compatConfig' | ||||
| import { DeprecationTypes, warnDeprecation } from './deprecations' | ||||
| import { getCompatListeners } from './instanceListeners' | ||||
| import { compatH } from './renderFn' | ||||
| 
 | ||||
| export function convertLegacyComponent(comp: any): Component { | ||||
|   // 2.x async component
 | ||||
|   if ( | ||||
|     isFunction(comp) && | ||||
|     softAssertCompatEnabled(DeprecationTypes.COMPONENT_ASYNC, comp) | ||||
|   ) { | ||||
|   // since after disabling this, plain functions are still valid usage, do not
 | ||||
|   // use softAssert here.
 | ||||
|   if (isFunction(comp) && isCompatEnabled(DeprecationTypes.COMPONENT_ASYNC)) { | ||||
|     __DEV__ && warnDeprecation(DeprecationTypes.COMPONENT_ASYNC, comp) | ||||
|     return convertLegacyAsyncComponent(comp) | ||||
|   } | ||||
| 
 | ||||
| @ -78,6 +87,56 @@ function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) { | ||||
|   return converted | ||||
| } | ||||
| 
 | ||||
| function convertLegacyFunctionalComponent(comp: ComponentOptions) { | ||||
|   return comp.render as FunctionalComponent | ||||
| const normalizedFunctionalComponentMap = new Map< | ||||
|   ComponentOptions, | ||||
|   FunctionalComponent | ||||
| >() | ||||
| 
 | ||||
| const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = { | ||||
|   get(target, key: string) { | ||||
|     const slot = target[key] | ||||
|     return slot && slot() | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function convertLegacyFunctionalComponent(comp: ComponentOptions) { | ||||
|   if (normalizedFunctionalComponentMap.has(comp)) { | ||||
|     return normalizedFunctionalComponentMap.get(comp)! | ||||
|   } | ||||
| 
 | ||||
|   const legacyFn = comp.render as any | ||||
| 
 | ||||
|   const Func: FunctionalComponent = (props, ctx) => { | ||||
|     const instance = getCurrentInstance()! | ||||
| 
 | ||||
|     const legacyCtx = { | ||||
|       props, | ||||
|       children: instance.vnode.children || [], | ||||
|       data: instance.vnode.props || {}, | ||||
|       scopedSlots: ctx.slots, | ||||
|       parent: instance.parent && instance.parent.proxy, | ||||
|       get slots() { | ||||
|         return new Proxy(ctx.slots, legacySlotProxyHandlers) | ||||
|       }, | ||||
|       get listeners() { | ||||
|         return getCompatListeners(instance) | ||||
|       }, | ||||
|       get injections() { | ||||
|         if (comp.inject) { | ||||
|           const injections = {} | ||||
|           resolveInjections(comp.inject, {}) | ||||
|           return injections | ||||
|         } | ||||
|         return {} | ||||
|       } | ||||
|     } | ||||
|     return legacyFn(compatH, legacyCtx) | ||||
|   } | ||||
|   Func.props = comp.props | ||||
|   Func.displayName = comp.name | ||||
|   // v2 functional components do not inherit attrs
 | ||||
|   Func.inheritAttrs = false | ||||
| 
 | ||||
|   normalizedFunctionalComponentMap.set(comp, Func) | ||||
|   return Func | ||||
| } | ||||
|  | ||||
| @ -314,7 +314,13 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = { | ||||
|           name ? ` <${name}>` : `s` | ||||
|         } should be explicitly created via \`defineAsyncComponent()\` ` + | ||||
|         `in Vue 3. Plain functions will be treated as functional components in ` + | ||||
|         `non-compat build.` | ||||
|         `non-compat build. If you have already migrated all async component ` + | ||||
|         `usage and intend to use plain functions for functional components, ` + | ||||
|         `you can disable the compat behavior and suppress this ` + | ||||
|         `warning with:` + | ||||
|         `\n\n  configureCompat({ ${ | ||||
|           DeprecationTypes.COMPONENT_ASYNC | ||||
|         }: false })\n` | ||||
|       ) | ||||
|     }, | ||||
|     link: `https://v3.vuejs.org/guide/migration/async-components.html` | ||||
| @ -327,13 +333,10 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = { | ||||
|         `Functional component${ | ||||
|           name ? ` <${name}>` : `s` | ||||
|         } should be defined as a plain function in Vue 3. The "functional" ` +
 | ||||
|         `option has been removed.\n` + | ||||
|         `NOTE: Before migrating, ensure that all async ` + | ||||
|         `components have been upgraded to use \`defineAsyncComponent()\` and ` + | ||||
|         `then disable compat for legacy async components with:` + | ||||
|         `\n\n  configureCompat({ ${ | ||||
|           DeprecationTypes.COMPONENT_ASYNC | ||||
|         }: false })\n` | ||||
|         `option has been removed. NOTE: Before migrating to use plain ` + | ||||
|         `functions for functional components, first make sure that all async ` + | ||||
|         `components usage have been migrated and its compat behavior has ` + | ||||
|         `been disabled.` | ||||
|       ) | ||||
|     }, | ||||
|     link: `https://v3.vuejs.org/guide/migration/functional-components.html` | ||||
|  | ||||
| @ -41,17 +41,21 @@ type LegacyVNodeChildren = | ||||
|   | VNode | ||||
|   | VNodeArrayChildren | ||||
| 
 | ||||
| export function h( | ||||
| export function compatH( | ||||
|   type: string | Component, | ||||
|   children?: LegacyVNodeChildren | ||||
| ): VNode | ||||
| export function h( | ||||
| export function compatH( | ||||
|   type: string | Component, | ||||
|   props?: LegacyVNodeProps, | ||||
|   children?: LegacyVNodeChildren | ||||
| ): VNode | ||||
| 
 | ||||
| export function h(type: any, propsOrChildren?: any, children?: any): VNode { | ||||
| export function compatH( | ||||
|   type: any, | ||||
|   propsOrChildren?: any, | ||||
|   children?: any | ||||
| ): VNode { | ||||
|   const l = arguments.length | ||||
|   if (l === 2) { | ||||
|     if (isObject(propsOrChildren) && !isArray(propsOrChildren)) { | ||||
| @ -85,7 +89,7 @@ export function h(type: any, propsOrChildren?: any, children?: any): VNode { | ||||
| 
 | ||||
| function convertLegacyProps(props: LegacyVNodeProps): Data & VNodeProps { | ||||
|   // TODO
 | ||||
|   return {} | ||||
|   return props as any | ||||
| } | ||||
| 
 | ||||
| function convertLegacyDirectives(vnode: VNode, props: LegacyVNodeProps): VNode { | ||||
|  | ||||
| @ -597,31 +597,7 @@ export function applyOptions( | ||||
|   // - watch (deferred since it relies on `this` access)
 | ||||
| 
 | ||||
|   if (injectOptions) { | ||||
|     if (isArray(injectOptions)) { | ||||
|       for (let i = 0; i < injectOptions.length; i++) { | ||||
|         const key = injectOptions[i] | ||||
|         ctx[key] = inject(key) | ||||
|         if (__DEV__) { | ||||
|           checkDuplicateProperties!(OptionTypes.INJECT, key) | ||||
|         } | ||||
|       } | ||||
|     } else { | ||||
|       for (const key in injectOptions) { | ||||
|         const opt = injectOptions[key] | ||||
|         if (isObject(opt)) { | ||||
|           ctx[key] = inject( | ||||
|             opt.from || key, | ||||
|             opt.default, | ||||
|             true /* treat default function as factory */ | ||||
|           ) | ||||
|         } else { | ||||
|           ctx[key] = inject(opt) | ||||
|         } | ||||
|         if (__DEV__) { | ||||
|           checkDuplicateProperties!(OptionTypes.INJECT, key) | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     resolveInjections(injectOptions, ctx, checkDuplicateProperties) | ||||
|   } | ||||
| 
 | ||||
|   if (methods) { | ||||
| @ -842,6 +818,38 @@ export function applyOptions( | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| export function resolveInjections( | ||||
|   injectOptions: ComponentInjectOptions, | ||||
|   ctx: any, | ||||
|   checkDuplicateProperties = NOOP as any | ||||
| ) { | ||||
|   if (isArray(injectOptions)) { | ||||
|     for (let i = 0; i < injectOptions.length; i++) { | ||||
|       const key = injectOptions[i] | ||||
|       ctx[key] = inject(key) | ||||
|       if (__DEV__) { | ||||
|         checkDuplicateProperties!(OptionTypes.INJECT, key) | ||||
|       } | ||||
|     } | ||||
|   } else { | ||||
|     for (const key in injectOptions) { | ||||
|       const opt = injectOptions[key] | ||||
|       if (isObject(opt)) { | ||||
|         ctx[key] = inject( | ||||
|           opt.from || key, | ||||
|           opt.default, | ||||
|           true /* treat default function as factory */ | ||||
|         ) | ||||
|       } else { | ||||
|         ctx[key] = inject(opt) | ||||
|       } | ||||
|       if (__DEV__) { | ||||
|         checkDuplicateProperties!(OptionTypes.INJECT, key) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function callSyncHook( | ||||
|   name: 'beforeCreate' | 'created', | ||||
|   type: LifecycleHooks, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user