diff --git a/packages/runtime-core/__tests__/vnode.spec.ts b/packages/runtime-core/__tests__/vnode.spec.ts index 5f27d99c4a4..aa0138a1738 100644 --- a/packages/runtime-core/__tests__/vnode.spec.ts +++ b/packages/runtime-core/__tests__/vnode.spec.ts @@ -148,6 +148,11 @@ describe('vnode', () => { ShapeFlags.ELEMENT | ShapeFlags.ARRAY_CHILDREN ) }) + + test('should skip null/undefined/boolean inside array children', () => { + const vnode = createVNode('div', null, [null, undefined, true]) + expect(vnode.children!.length).toBe(0) + }) }) test('normalizeVNode', () => { diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index b1d02603dc5..d07e5ce5014 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -488,16 +488,13 @@ export function createCommentVNode( } export function normalizeVNode(child: VNodeChild): VNode { - if (child == null || typeof child === 'boolean') { - // empty placeholder - return createVNode(Comment) - } else if (isArray(child)) { + if (isArray(child)) { // fragment return createVNode(Fragment, null, child) } else if (typeof child === 'object') { // already vnode, this should be the most common since compiled templates // always produce all-vnode children arrays - return child.el === null ? child : cloneVNode(child) + return child!.el === null ? child! : cloneVNode(child!) } else { // strings and numbers return createVNode(Text, null, String(child)) @@ -545,10 +542,19 @@ export function normalizeChildren(vnode: VNode, children: unknown) { type = ShapeFlags.TEXT_CHILDREN } } + + if (isArray(children)) { + children = normalizeArrayChildren(children) + } + vnode.children = children as VNodeNormalizedChildren vnode.shapeFlag |= type } +function normalizeArrayChildren(children: VNodeArrayChildren) { + return children.filter(child => child != null && typeof child !== 'boolean') +} + const handlersRE = /^on|^vnode/ export function mergeProps(...args: (Data & VNodeProps)[]) {