fix(runtime-core): do not use bail patchFlag on cloned vnodes

fix #1665

- cloned vnodes with extra props will receive only the full props flag
- this commit affects `cloneVNode` behavior when used in manual render
  functions.
  - ok for normal elements since elements only use patchFlags for own
    props optimization
  - full props flag is skipped for fragments because fragments use
    patchFlags only for children optimization
  - this also affects `shouldUpdateComponent` where it should now only
    respect patchFlags in optimized mode, since component vnodes use
    the patchFlag for both props and slots optimization checks.
This commit is contained in:
Evan You 2020-07-21 12:35:34 -04:00
parent 324167d3d2
commit 6390ddfb7d
2 changed files with 13 additions and 17 deletions

View File

@ -279,7 +279,7 @@ export function shouldUpdateComponent(
return true return true
} }
if (patchFlag > 0) { if (optimized && patchFlag > 0) {
if (patchFlag & PatchFlags.DYNAMIC_SLOTS) { if (patchFlag & PatchFlags.DYNAMIC_SLOTS) {
// slot content that references values that might have changed, // slot content that references values that might have changed,
// e.g. in a v-for // e.g. in a v-for
@ -300,7 +300,7 @@ export function shouldUpdateComponent(
} }
} }
} }
} else if (!optimized) { } else {
// this path is only taken by manually written render functions // this path is only taken by manually written render functions
// so presence of any children leads to a forced update // so presence of any children leads to a forced update
if (prevChildren || nextChildren) { if (prevChildren || nextChildren) {

View File

@ -310,7 +310,11 @@ function _createVNode(
} }
if (isVNode(type)) { if (isVNode(type)) {
return cloneVNode(type, props, children) const cloned = cloneVNode(type, props)
if (children) {
normalizeChildren(cloned, children)
}
return cloned
} }
// class component normalization. // class component normalization.
@ -420,8 +424,7 @@ function _createVNode(
export function cloneVNode<T, U>( export function cloneVNode<T, U>(
vnode: VNode<T, U>, vnode: VNode<T, U>,
extraProps?: Data & VNodeProps | null, extraProps?: Data & VNodeProps | null
children?: unknown
): VNode<T, U> { ): VNode<T, U> {
const props = extraProps const props = extraProps
? vnode.props ? vnode.props
@ -430,7 +433,7 @@ export function cloneVNode<T, U>(
: vnode.props : vnode.props
// This is intentionally NOT using spread or extend to avoid the runtime // This is intentionally NOT using spread or extend to avoid the runtime
// key enumeration cost. // key enumeration cost.
const cloned: VNode<T, U> = { return {
__v_isVNode: true, __v_isVNode: true,
__v_skip: true, __v_skip: true,
type: vnode.type, type: vnode.type,
@ -444,14 +447,11 @@ export function cloneVNode<T, U>(
staticCount: vnode.staticCount, staticCount: vnode.staticCount,
shapeFlag: vnode.shapeFlag, shapeFlag: vnode.shapeFlag,
// if the vnode is cloned with extra props, we can no longer assume its // if the vnode is cloned with extra props, we can no longer assume its
// existing patch flag to be reliable and need to bail out of optimized mode. // existing patch flag to be reliable and need to add the FULL_PROPS flag.
// however we don't want block nodes to de-opt their children, so if the patchFlag:
// vnode is a block node, we only add the FULL_PROPS flag to it. extraProps && vnode.type !== Fragment
patchFlag: extraProps
? vnode.dynamicChildren
? vnode.patchFlag | PatchFlags.FULL_PROPS ? vnode.patchFlag | PatchFlags.FULL_PROPS
: PatchFlags.BAIL : vnode.patchFlag,
: vnode.patchFlag,
dynamicProps: vnode.dynamicProps, dynamicProps: vnode.dynamicProps,
dynamicChildren: vnode.dynamicChildren, dynamicChildren: vnode.dynamicChildren,
appContext: vnode.appContext, appContext: vnode.appContext,
@ -467,10 +467,6 @@ export function cloneVNode<T, U>(
el: vnode.el, el: vnode.el,
anchor: vnode.anchor anchor: vnode.anchor
} }
if (children) {
normalizeChildren(cloned, children)
}
return cloned
} }
/** /**