diff --git a/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts b/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts
index d8088454..6f8957d0 100644
--- a/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts
+++ b/packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts
@@ -8,7 +8,9 @@ import {
resolveDynamicComponent,
h,
serializeInner,
- createVNode
+ createVNode,
+ Comment,
+ VNode
} from '@vue/runtime-test'
import { mockWarn } from '@vue/shared'
@@ -102,6 +104,7 @@ describe('resolveAssets', () => {
baz: { render: () => 'baz' }
}
let foo, bar, baz // dynamic components
+ let dynamicVNode: VNode
const Child = {
render(this: any) {
@@ -115,6 +118,7 @@ describe('resolveAssets', () => {
return () => {
foo = resolveDynamicComponent('foo') //
bar = resolveDynamicComponent(dynamicComponents.bar) // , function
+ dynamicVNode = createVNode(resolveDynamicComponent(null)) //
return h(Child, () => {
// check inside child slots
baz = resolveDynamicComponent(dynamicComponents.baz) // , object
@@ -129,6 +133,8 @@ describe('resolveAssets', () => {
expect(foo).toBe(dynamicComponents.foo)
expect(bar).toBe(dynamicComponents.bar)
expect(baz).toBe(dynamicComponents.baz)
+ // should allow explicit falsy type to remove the component
+ expect(dynamicVNode!.type).toBe(Comment)
})
test('resolve dynamic component should fallback to plain element without warning', () => {
diff --git a/packages/runtime-core/src/helpers/resolveAssets.ts b/packages/runtime-core/src/helpers/resolveAssets.ts
index 8cb134d5..f7118a88 100644
--- a/packages/runtime-core/src/helpers/resolveAssets.ts
+++ b/packages/runtime-core/src/helpers/resolveAssets.ts
@@ -6,13 +6,7 @@ import {
ComponentOptions
} from '../component'
import { Directive } from '../directives'
-import {
- camelize,
- capitalize,
- isString,
- isObject,
- isFunction
-} from '@vue/shared'
+import { camelize, capitalize, isString, isObject } from '@vue/shared'
import { warn } from '../warning'
const COMPONENTS = 'components'
@@ -22,14 +16,16 @@ export function resolveComponent(name: string): Component | string | undefined {
return resolveAsset(COMPONENTS, name) || name
}
+export const NULL_DYNAMIC_COMPONENT = Symbol()
+
export function resolveDynamicComponent(
component: unknown
-): Component | string | undefined {
- if (!component) return
+): Component | string | typeof NULL_DYNAMIC_COMPONENT {
if (isString(component)) {
return resolveAsset(COMPONENTS, component, false) || component
- } else if (isFunction(component) || isObject(component)) {
- return component
+ } else {
+ // invalid types will fallthrough to createVNode and raise warning
+ return (component as any) || NULL_DYNAMIC_COMPONENT
}
}
diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts
index f4de40ef..3b21c6ab 100644
--- a/packages/runtime-core/src/vnode.ts
+++ b/packages/runtime-core/src/vnode.ts
@@ -31,6 +31,7 @@ import { currentScopeId } from './helpers/scopeId'
import { TeleportImpl, isTeleport } from './components/Teleport'
import { currentRenderingInstance } from './componentRenderUtils'
import { RendererNode, RendererElement } from './renderer'
+import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets'
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
__isFragment: true
@@ -254,15 +255,15 @@ export const createVNode = (__DEV__
: _createVNode) as typeof _createVNode
function _createVNode(
- type: VNodeTypes | ClassComponent,
+ type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
props: (Data & VNodeProps) | null = null,
children: unknown = null,
patchFlag: number = 0,
dynamicProps: string[] | null = null,
isBlockNode = false
): VNode {
- if (!type) {
- if (__DEV__) {
+ if (!type || type === NULL_DYNAMIC_COMPONENT) {
+ if (__DEV__ && !type) {
warn(`Invalid vnode type when creating vnode: ${type}.`)
}
type = Comment