Skip to content

Commit

Permalink
feat(runtime-core): support creating vnode from existing vnode
Browse files Browse the repository at this point in the history
This allows passing vnode around with curried props and use it in
places where VNodeType is expected, e.g. `<component :is=""/>`
  • Loading branch information
yyx990803 committed Jul 1, 2020
1 parent 359b4a3 commit c9629f2
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 3 deletions.
14 changes: 14 additions & 0 deletions packages/runtime-core/__tests__/vnode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ describe('vnode', () => {
expect(vnode.props).toBe(null)
})

test('create from an existing vnode', () => {
const vnode1 = createVNode('p', { id: 'foo' })
const vnode2 = createVNode(vnode1, { class: 'bar' }, 'baz')
expect(vnode2).toMatchObject({
type: 'p',
props: {
id: 'foo',
class: 'bar'
},
children: 'baz',
shapeFlag: ShapeFlags.ELEMENT | ShapeFlags.TEXT_CHILDREN
})
})

test('vnode keys', () => {
for (const key of ['', 'a', 0, 1, NaN]) {
expect(createVNode('div', { key }).key).toBe(key)
Expand Down
15 changes: 12 additions & 3 deletions packages/runtime-core/src/vnode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ export const createVNode = (__DEV__
: _createVNode) as typeof _createVNode

function _createVNode(
type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
type: VNode | VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
props: (Data & VNodeProps) | null = null,
children: unknown = null,
patchFlag: number = 0,
Expand All @@ -304,6 +304,10 @@ function _createVNode(
type = Comment
}

if (isVNode(type)) {
return cloneVNode(type, props, children)
}

// class component normalization.
if (isFunction(type) && '__vccOpts' in type) {
type = type.__vccOpts
Expand Down Expand Up @@ -406,7 +410,8 @@ function _createVNode(

export function cloneVNode<T, U>(
vnode: VNode<T, U>,
extraProps?: Data & VNodeProps
extraProps?: Data & VNodeProps | null,
children?: unknown
): VNode<T, U> {
const props = extraProps
? vnode.props
Expand All @@ -415,7 +420,7 @@ export function cloneVNode<T, U>(
: vnode.props
// This is intentionally NOT using spread or extend to avoid the runtime
// key enumeration cost.
return {
const cloned: VNode<T, U> = {
__v_isVNode: true,
__v_skip: true,
type: vnode.type,
Expand Down Expand Up @@ -452,6 +457,10 @@ export function cloneVNode<T, U>(
el: vnode.el,
anchor: vnode.anchor
}
if (children) {
normalizeChildren(cloned, children)
}
return cloned
}

/**
Expand Down

3 comments on commit c9629f2

@jods4
Copy link
Contributor

@jods4 jods4 commented on c9629f2 Jul 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yyx990803 What's the intended difference between this API and cloneVNode that was previously available?
Is it to consolidate apis into less functions (i.e. replace cloneVNode with createVNode)?

@yyx990803
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. This essentially enables "props currying" by passing around a component vnode with pre-injected props, but using it as the component itself.

@jods4
Copy link
Contributor

@jods4 jods4 commented on c9629f2 Jul 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks. Interesting idea.

Please sign in to comment.