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 { isArray, isFunction, isObject, isPromise } from '@vue/shared' | ||||||
| import { defineAsyncComponent } from '../apiAsyncComponent' | 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 { isVNode } from '../vnode' | ||||||
| import { softAssertCompatEnabled } from './compatConfig' | import { isCompatEnabled, softAssertCompatEnabled } from './compatConfig' | ||||||
| import { DeprecationTypes } from './deprecations' | import { DeprecationTypes, warnDeprecation } from './deprecations' | ||||||
|  | import { getCompatListeners } from './instanceListeners' | ||||||
|  | import { compatH } from './renderFn' | ||||||
| 
 | 
 | ||||||
| export function convertLegacyComponent(comp: any): Component { | export function convertLegacyComponent(comp: any): Component { | ||||||
|   // 2.x async component
 |   // 2.x async component
 | ||||||
|   if ( |   // since after disabling this, plain functions are still valid usage, do not
 | ||||||
|     isFunction(comp) && |   // use softAssert here.
 | ||||||
|     softAssertCompatEnabled(DeprecationTypes.COMPONENT_ASYNC, comp) |   if (isFunction(comp) && isCompatEnabled(DeprecationTypes.COMPONENT_ASYNC)) { | ||||||
|   ) { |     __DEV__ && warnDeprecation(DeprecationTypes.COMPONENT_ASYNC, comp) | ||||||
|     return convertLegacyAsyncComponent(comp) |     return convertLegacyAsyncComponent(comp) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -78,6 +87,56 @@ function convertLegacyAsyncComponent(comp: LegacyAsyncComponent) { | |||||||
|   return converted |   return converted | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function convertLegacyFunctionalComponent(comp: ComponentOptions) { | const normalizedFunctionalComponentMap = new Map< | ||||||
|   return comp.render as FunctionalComponent |   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` |           name ? ` <${name}>` : `s` | ||||||
|         } should be explicitly created via \`defineAsyncComponent()\` ` + |         } should be explicitly created via \`defineAsyncComponent()\` ` + | ||||||
|         `in Vue 3. Plain functions will be treated as functional components in ` + |         `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` |     link: `https://v3.vuejs.org/guide/migration/async-components.html` | ||||||
| @ -327,13 +333,10 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = { | |||||||
|         `Functional component${ |         `Functional component${ | ||||||
|           name ? ` <${name}>` : `s` |           name ? ` <${name}>` : `s` | ||||||
|         } should be defined as a plain function in Vue 3. The "functional" ` +
 |         } should be defined as a plain function in Vue 3. The "functional" ` +
 | ||||||
|         `option has been removed.\n` + |         `option has been removed. NOTE: Before migrating to use plain ` + | ||||||
|         `NOTE: Before migrating, ensure that all async ` + |         `functions for functional components, first make sure that all async ` + | ||||||
|         `components have been upgraded to use \`defineAsyncComponent()\` and ` + |         `components usage have been migrated and its compat behavior has ` + | ||||||
|         `then disable compat for legacy async components with:` + |         `been disabled.` | ||||||
|         `\n\n  configureCompat({ ${ |  | ||||||
|           DeprecationTypes.COMPONENT_ASYNC |  | ||||||
|         }: false })\n` |  | ||||||
|       ) |       ) | ||||||
|     }, |     }, | ||||||
|     link: `https://v3.vuejs.org/guide/migration/functional-components.html` |     link: `https://v3.vuejs.org/guide/migration/functional-components.html` | ||||||
|  | |||||||
| @ -41,17 +41,21 @@ type LegacyVNodeChildren = | |||||||
|   | VNode |   | VNode | ||||||
|   | VNodeArrayChildren |   | VNodeArrayChildren | ||||||
| 
 | 
 | ||||||
| export function h( | export function compatH( | ||||||
|   type: string | Component, |   type: string | Component, | ||||||
|   children?: LegacyVNodeChildren |   children?: LegacyVNodeChildren | ||||||
| ): VNode | ): VNode | ||||||
| export function h( | export function compatH( | ||||||
|   type: string | Component, |   type: string | Component, | ||||||
|   props?: LegacyVNodeProps, |   props?: LegacyVNodeProps, | ||||||
|   children?: LegacyVNodeChildren |   children?: LegacyVNodeChildren | ||||||
| ): VNode | ): 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 |   const l = arguments.length | ||||||
|   if (l === 2) { |   if (l === 2) { | ||||||
|     if (isObject(propsOrChildren) && !isArray(propsOrChildren)) { |     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 { | function convertLegacyProps(props: LegacyVNodeProps): Data & VNodeProps { | ||||||
|   // TODO
 |   // TODO
 | ||||||
|   return {} |   return props as any | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function convertLegacyDirectives(vnode: VNode, props: LegacyVNodeProps): VNode { | function convertLegacyDirectives(vnode: VNode, props: LegacyVNodeProps): VNode { | ||||||
|  | |||||||
| @ -597,31 +597,7 @@ export function applyOptions( | |||||||
|   // - watch (deferred since it relies on `this` access)
 |   // - watch (deferred since it relies on `this` access)
 | ||||||
| 
 | 
 | ||||||
|   if (injectOptions) { |   if (injectOptions) { | ||||||
|     if (isArray(injectOptions)) { |     resolveInjections(injectOptions, ctx, checkDuplicateProperties) | ||||||
|       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) |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (methods) { |   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( | function callSyncHook( | ||||||
|   name: 'beforeCreate' | 'created', |   name: 'beforeCreate' | 'created', | ||||||
|   type: LifecycleHooks, |   type: LifecycleHooks, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user