wip: shouldUpdateComponent logic for slots
This commit is contained in:
parent
333ceaa4b5
commit
e0a66d0381
@ -8,7 +8,7 @@ import {
|
||||
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 }
|
||||
|
||||
@ -183,15 +183,18 @@ export function shouldUpdateComponent(
|
||||
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]
|
||||
@ -201,7 +204,11 @@ export function shouldUpdateComponent(
|
||||
}
|
||||
}
|
||||
} 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
|
||||
}
|
||||
@ -211,16 +218,21 @@ export function shouldUpdateComponent(
|
||||
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
|
||||
}
|
||||
|
@ -22,7 +22,15 @@ import {
|
||||
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'
|
||||
@ -223,45 +231,50 @@ export function createRenderer(options: RendererOptions) {
|
||||
// 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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +29,18 @@ export const STYLE = 1 << 2
|
||||
// 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
|
||||
|
Loading…
Reference in New Issue
Block a user