diff --git a/packages/runtime-core/__tests__/h.spec.ts b/packages/runtime-core/__tests__/h.spec.ts
index b71958d8..b55c9f48 100644
--- a/packages/runtime-core/__tests__/h.spec.ts
+++ b/packages/runtime-core/__tests__/h.spec.ts
@@ -18,8 +18,12 @@ describe('renderer: h', () => {
// array
expect(h('div', ['foo'])).toMatchObject(createVNode('div', null, ['foo']))
// default slot
+ const Component = { template: '
' }
const slot = () => {}
- expect(h('div', slot)).toMatchObject(createVNode('div', null, slot))
+ expect(h(Component, slot)).toMatchObject(createVNode(Component, null, slot))
+ // single vnode
+ const vnode = h('div')
+ expect(h('div', vnode)).toMatchObject(createVNode('div', null, [vnode]))
// text
expect(h('div', 'foo')).toMatchObject(createVNode('div', null, 'foo'))
})
@@ -28,20 +32,27 @@ describe('renderer: h', () => {
// array
expect(h('div', {}, ['foo'])).toMatchObject(createVNode('div', {}, ['foo']))
// default slot
+ const Component = { template: '
' }
const slot = () => {}
- expect(h('div', {}, slot)).toMatchObject(createVNode('div', {}, slot))
+ expect(h(Component, {}, slot)).toMatchObject(
+ createVNode(Component, {}, slot)
+ )
+ // single vnode
+ const vnode = h('div')
+ expect(h('div', {}, vnode)).toMatchObject(createVNode('div', {}, [vnode]))
// text
expect(h('div', {}, 'foo')).toMatchObject(createVNode('div', {}, 'foo'))
})
test('named slots with null props', () => {
+ const Component = { template: '
' }
const slot = () => {}
expect(
- h('div', null, {
+ h(Component, null, {
foo: slot
})
).toMatchObject(
- createVNode('div', null, {
+ createVNode(Component, null, {
foo: slot
})
)
diff --git a/packages/runtime-core/src/h.ts b/packages/runtime-core/src/h.ts
index 5b32f3b7..eebee368 100644
--- a/packages/runtime-core/src/h.ts
+++ b/packages/runtime-core/src/h.ts
@@ -4,7 +4,8 @@ import {
createVNode,
VNodeChildren,
Fragment,
- Portal
+ Portal,
+ isVNode
} from './vnode'
import { isObject, isArray } from '@vue/shared'
import { Ref } from '@vue/reactivity'
@@ -34,17 +35,19 @@ h('div', {})
// type + omit props + children
// Omit props does NOT support named slots
h('div', []) // array
-h('div', () => {}) // default slot
h('div', 'foo') // text
+h('div', h('br')) // vnode
+h(Component, () => {}) // default slot
// type + props + children
h('div', {}, []) // array
-h('div', {}, () => {}) // default slot
-h('div', {}, {}) // named slots
h('div', {}, 'foo') // text
+h('div', {}, h('br')) // vnode
+h(Component, {}, () => {}) // default slot
+h(Component, {}, {}) // named slots
// named slots without props requires explicit `null` to avoid ambiguity
-h('div', null, {})
+h(Component, null, {})
**/
export interface RawProps {
@@ -61,6 +64,7 @@ export type RawChildren =
| string
| number
| boolean
+ | VNode
| VNodeChildren
| (() => any)
@@ -142,6 +146,10 @@ export function h(
): VNode {
if (arguments.length === 2) {
if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
+ // single vnode without props
+ if (isVNode(propsOrChildren)) {
+ return createVNode(type, null, [propsOrChildren])
+ }
// props without children
return createVNode(type, propsOrChildren)
} else {
@@ -149,6 +157,9 @@ export function h(
return createVNode(type, null, propsOrChildren)
}
} else {
+ if (isVNode(children)) {
+ children = [children]
+ }
return createVNode(type, propsOrChildren, children)
}
}
diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts
index 2229fbcd..9abb4476 100644
--- a/packages/runtime-core/src/vnode.ts
+++ b/packages/runtime-core/src/vnode.ts
@@ -129,7 +129,7 @@ export function createBlock(
return vnode
}
-export function isVNode(value: any): boolean {
+export function isVNode(value: any): value is VNode {
return value ? value._isVNode === true : false
}