import { isFunction, EMPTY_OBJ } from '@vue/shared'
import { RenderProxyHandlers } from './componentProxy'
import { ComponentPropsOptions, PropValidator } from './componentProps'
-import { PROPS, SLOTS } from './patchFlags'
+import { PROPS, DYNAMIC_SLOTS, FULL_PROPS } from './patchFlags'
export type Data = { [key: string]: any }
prevVNode: VNode,
nextVNode: VNode
): boolean {
- const { props: prevProps } = prevVNode
- const { props: nextProps, patchFlag } = nextVNode
+ const { props: prevProps, children: prevChildren } = prevVNode
+ const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
if (patchFlag !== null) {
- if (patchFlag & SLOTS) {
+ if (patchFlag & DYNAMIC_SLOTS) {
// slot content that references values that might have changed,
// e.g. in a v-for
return true
}
- if (patchFlag & PROPS) {
+ if (patchFlag & FULL_PROPS) {
+ // presence of this flag indicates props are always non-null
+ return hasPropsChanged(prevProps as Data, nextProps as Data)
+ } else if (patchFlag & PROPS) {
const dynamicProps = nextVNode.dynamicProps as string[]
for (let i = 0; i < dynamicProps.length; i++) {
const key = dynamicProps[i]
}
}
} else {
- // TODO handle slots
+ // this path is only taken by manually written render functions
+ // so presence of any children leads to a forced update
+ if (prevChildren != null || nextChildren != null) {
+ return true
+ }
if (prevProps === nextProps) {
return false
}
if (nextProps === null) {
return prevProps !== null
}
- const nextKeys = Object.keys(nextProps)
- if (nextKeys.length !== Object.keys(prevProps).length) {
+ return hasPropsChanged(prevProps, nextProps)
+ }
+ return false
+}
+
+function hasPropsChanged(prevProps: Data, nextProps: Data): boolean {
+ const nextKeys = Object.keys(nextProps)
+ if (nextKeys.length !== Object.keys(prevProps).length) {
+ return true
+ }
+ for (let i = 0; i < nextKeys.length; i++) {
+ const key = nextKeys[i]
+ if (nextProps[key] !== prevProps[key]) {
return true
}
- for (let i = 0; i < nextKeys.length; i++) {
- const key = nextKeys[i]
- if (nextProps[key] !== prevProps[key]) {
- return true
- }
- }
}
return false
}
EMPTY_OBJ,
EMPTY_ARR
} from '@vue/shared'
-import { TEXT, CLASS, STYLE, PROPS, KEYED, UNKEYED } from './patchFlags'
+import {
+ TEXT,
+ CLASS,
+ STYLE,
+ PROPS,
+ KEYED,
+ UNKEYED,
+ FULL_PROPS
+} from './patchFlags'
import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
import { effect, stop, ReactiveEffectOptions } from '@vue/observer'
import { resolveProps } from './componentProps'
// in this path old node and new node are guaranteed to have the same shape
// (i.e. at the exact same position in the source template)
- // class
- // this flag is matched when the element has dynamic class bindings.
- if (patchFlag & CLASS) {
- // TODO handle full class API, potentially optimize at compilation stage?
- if (oldProps.class !== newProps.class) {
- hostPatchProp(el, 'class', newProps.class, null, false)
+ if (patchFlag & FULL_PROPS) {
+ // element props contain dynamic keys, full diff needed
+ patchProps(el, n2, oldProps, newProps)
+ } else {
+ // class
+ // this flag is matched when the element has dynamic class bindings.
+ if (patchFlag & CLASS) {
+ // TODO handle full class API, potentially optimize at compilation stage?
+ if (oldProps.class !== newProps.class) {
+ hostPatchProp(el, 'class', newProps.class, null, false)
+ }
}
- }
- // style
- // this flag is matched when the element has dynamic style bindings
- // TODO separate static and dynamic styles?
- if (patchFlag & STYLE) {
- hostPatchProp(el, 'style', newProps.style, oldProps.style, false)
- }
+ // style
+ // this flag is matched when the element has dynamic style bindings
+ // TODO separate static and dynamic styles?
+ if (patchFlag & STYLE) {
+ hostPatchProp(el, 'style', newProps.style, oldProps.style, false)
+ }
- // props
- // This flag is matched when the element has dynamic prop/attr bindings
- // other than class and style. The keys of dynamic prop/attrs are saved for
- // faster iteration.
- // Note dynamic keys like :[foo]="bar" will cause this optimization to
- // bail out and go through a full diff because we need to unset the old key
- if (patchFlag & PROPS) {
- // if the flag is present then dynamicProps must be non-null
- const propsToUpdate = n2.dynamicProps as string[]
- for (let i = 0; i < propsToUpdate.length; i++) {
- const key = propsToUpdate[i]
- const prev = oldProps[key]
- const next = newProps[key]
- if (prev !== next) {
- hostPatchProp(
- el,
- key,
- next,
- prev,
- false,
- n1.children as VNode[],
- unmountChildren
- )
+ // props
+ // This flag is matched when the element has dynamic prop/attr bindings
+ // other than class and style. The keys of dynamic prop/attrs are saved for
+ // faster iteration.
+ // Note dynamic keys like :[foo]="bar" will cause this optimization to
+ // bail out and go through a full diff because we need to unset the old key
+ if (patchFlag & PROPS) {
+ // if the flag is present then dynamicProps must be non-null
+ const propsToUpdate = n2.dynamicProps as string[]
+ for (let i = 0; i < propsToUpdate.length; i++) {
+ const key = propsToUpdate[i]
+ const prev = oldProps[key]
+ const next = newProps[key]
+ if (prev !== next) {
+ hostPatchProp(
+ el,
+ key,
+ next,
+ prev,
+ false,
+ n1.children as VNode[],
+ unmountChildren
+ )
+ }
}
}
}
// them faster (without having to worry about removed props)
export const PROPS = 1 << 3
+// Indicates an element with props with dynamic keys. When keys change, a full
+// diff is always needed to remove the old key. This flag is mutually exclusive
+// with CLASS, STYLE and PROPS.
+export const FULL_PROPS = 1 << 4
+
// Indicates a fragment or element with keyed or partially-keyed v-for children
-export const KEYED = 1 << 4
+export const KEYED = 1 << 5
// Indicates a fragment or element that contains unkeyed v-for children
-export const UNKEYED = 1 << 5
+export const UNKEYED = 1 << 6
-// Indicates a component with dynamic slot (e.g. slot that references a v-for
-// iterated value). Components with this flag are always force updated.
-export const SLOTS = 1 << 6
+// Indicates a component with dynamic slots (e.g. slot that references a v-for
+// iterated value, or dynamic slot names).
+// Components with this flag are always force updated.
+export const DYNAMIC_SLOTS = 1 << 7