diff --git a/packages/runtime-core/__tests__/vnode.spec.ts b/packages/runtime-core/__tests__/vnode.spec.ts index 8895938182f..040e2f6bb21 100644 --- a/packages/runtime-core/__tests__/vnode.spec.ts +++ b/packages/runtime-core/__tests__/vnode.spec.ts @@ -46,6 +46,15 @@ describe('vnode', () => { } }) + test('create with class component', () => { + class Component { + $props: any + static __vccOpts = { template: '
' } + } + const vnode = createVNode(Component) + expect(vnode.type).toEqual(Component.__vccOpts) + }) + describe('class normalization', () => { test('string', () => { const vnode = createVNode('p', { class: 'foo baz' }) diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 5bccb756d32..bf28cf552db 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -58,13 +58,18 @@ export interface FunctionalComponent

extends SFCInternalOptions { displayName?: string } +export interface ClassComponent { + new (...args: any[]): ComponentPublicInstance + __vccOpts: ComponentOptions +} + export type Component = ComponentOptions | FunctionalComponent // A type used in public APIs where a component type is expected. // The constructor type is an artificial type returned by defineComponent(). export type PublicAPIComponent = | Component - | { new (): ComponentPublicInstance } + | { new (...args: any[]): ComponentPublicInstance } export { ComponentOptions } diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts index 30cb7a90fc5..e6f07fb1f1f 100644 --- a/packages/runtime-core/src/h.ts +++ b/packages/runtime-core/src/h.ts @@ -130,7 +130,7 @@ export function h( children?: RawChildren | RawSlots ): VNode -// fake constructor type returned by `defineComponent` +// fake constructor type returned by `defineComponent` or class component export function h(type: Constructor, children?: RawChildren): VNode export function h

( type: Constructor

, diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index e122091145d..272731e8a05 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -14,7 +14,8 @@ import { ComponentInternalInstance, Data, SetupProxySymbol, - Component + Component, + ClassComponent } from './component' import { RawSlots } from './componentSlots' import { isReactive, Ref } from '@vue/reactivity' @@ -162,7 +163,7 @@ export function setBlockTracking(value: number) { // A block root keeps track of dynamic nodes within the block in the // `dynamicChildren` array. export function createBlock( - type: VNodeTypes, + type: VNodeTypes | ClassComponent, props?: { [key: string]: any } | null, children?: any, patchFlag?: number, @@ -203,7 +204,7 @@ export function isSameVNodeType(n1: VNode, n2: VNode): boolean { } export function createVNode( - type: VNodeTypes, + type: VNodeTypes | ClassComponent, props: (Data & VNodeProps) | null = null, children: unknown = null, patchFlag: number = 0, @@ -216,6 +217,11 @@ export function createVNode( type = Comment } + // class component normalization. + if (isFunction(type) && '__vccOpts' in type) { + type = type.__vccOpts + } + // class & style normalization. if (props !== null) { // for reactive or proxy objects, we need to clone it to enable mutation.