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 { isFunction, EMPTY_OBJ } from '@vue/shared'
|
||||||
import { RenderProxyHandlers } from './componentProxy'
|
import { RenderProxyHandlers } from './componentProxy'
|
||||||
import { ComponentPropsOptions, PropValidator } from './componentProps'
|
import { ComponentPropsOptions, PropValidator } from './componentProps'
|
||||||
import { PROPS, SLOTS } from './patchFlags'
|
import { PROPS, DYNAMIC_SLOTS, FULL_PROPS } from './patchFlags'
|
||||||
|
|
||||||
export type Data = { [key: string]: any }
|
export type Data = { [key: string]: any }
|
||||||
|
|
||||||
@ -183,15 +183,18 @@ export function shouldUpdateComponent(
|
|||||||
prevVNode: VNode,
|
prevVNode: VNode,
|
||||||
nextVNode: VNode
|
nextVNode: VNode
|
||||||
): boolean {
|
): boolean {
|
||||||
const { props: prevProps } = prevVNode
|
const { props: prevProps, children: prevChildren } = prevVNode
|
||||||
const { props: nextProps, patchFlag } = nextVNode
|
const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
|
||||||
if (patchFlag !== null) {
|
if (patchFlag !== null) {
|
||||||
if (patchFlag & SLOTS) {
|
if (patchFlag & 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
|
||||||
return true
|
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[]
|
const dynamicProps = nextVNode.dynamicProps as string[]
|
||||||
for (let i = 0; i < dynamicProps.length; i++) {
|
for (let i = 0; i < dynamicProps.length; i++) {
|
||||||
const key = dynamicProps[i]
|
const key = dynamicProps[i]
|
||||||
@ -201,7 +204,11 @@ export function shouldUpdateComponent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (prevProps === nextProps) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -211,16 +218,21 @@ export function shouldUpdateComponent(
|
|||||||
if (nextProps === null) {
|
if (nextProps === null) {
|
||||||
return prevProps !== null
|
return prevProps !== null
|
||||||
}
|
}
|
||||||
const nextKeys = Object.keys(nextProps)
|
return hasPropsChanged(prevProps, nextProps)
|
||||||
if (nextKeys.length !== Object.keys(prevProps).length) {
|
}
|
||||||
|
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
|
return true
|
||||||
}
|
}
|
||||||
for (let i = 0; i < nextKeys.length; i++) {
|
|
||||||
const key = nextKeys[i]
|
|
||||||
if (nextProps[key] !== prevProps[key]) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,15 @@ import {
|
|||||||
EMPTY_OBJ,
|
EMPTY_OBJ,
|
||||||
EMPTY_ARR
|
EMPTY_ARR
|
||||||
} from '@vue/shared'
|
} 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 { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
|
||||||
import { effect, stop, ReactiveEffectOptions } from '@vue/observer'
|
import { effect, stop, ReactiveEffectOptions } from '@vue/observer'
|
||||||
import { resolveProps } from './componentProps'
|
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
|
// 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)
|
// (i.e. at the exact same position in the source template)
|
||||||
|
|
||||||
// class
|
if (patchFlag & FULL_PROPS) {
|
||||||
// this flag is matched when the element has dynamic class bindings.
|
// element props contain dynamic keys, full diff needed
|
||||||
if (patchFlag & CLASS) {
|
patchProps(el, n2, oldProps, newProps)
|
||||||
// TODO handle full class API, potentially optimize at compilation stage?
|
} else {
|
||||||
if (oldProps.class !== newProps.class) {
|
// class
|
||||||
hostPatchProp(el, 'class', newProps.class, null, false)
|
// 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
|
// style
|
||||||
// this flag is matched when the element has dynamic style bindings
|
// this flag is matched when the element has dynamic style bindings
|
||||||
// TODO separate static and dynamic styles?
|
// TODO separate static and dynamic styles?
|
||||||
if (patchFlag & STYLE) {
|
if (patchFlag & STYLE) {
|
||||||
hostPatchProp(el, 'style', newProps.style, oldProps.style, false)
|
hostPatchProp(el, 'style', newProps.style, oldProps.style, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// props
|
// props
|
||||||
// This flag is matched when the element has dynamic prop/attr bindings
|
// 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
|
// other than class and style. The keys of dynamic prop/attrs are saved for
|
||||||
// faster iteration.
|
// faster iteration.
|
||||||
// Note dynamic keys like :[foo]="bar" will cause this optimization to
|
// 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
|
// bail out and go through a full diff because we need to unset the old key
|
||||||
if (patchFlag & PROPS) {
|
if (patchFlag & PROPS) {
|
||||||
// if the flag is present then dynamicProps must be non-null
|
// if the flag is present then dynamicProps must be non-null
|
||||||
const propsToUpdate = n2.dynamicProps as string[]
|
const propsToUpdate = n2.dynamicProps as string[]
|
||||||
for (let i = 0; i < propsToUpdate.length; i++) {
|
for (let i = 0; i < propsToUpdate.length; i++) {
|
||||||
const key = propsToUpdate[i]
|
const key = propsToUpdate[i]
|
||||||
const prev = oldProps[key]
|
const prev = oldProps[key]
|
||||||
const next = newProps[key]
|
const next = newProps[key]
|
||||||
if (prev !== next) {
|
if (prev !== next) {
|
||||||
hostPatchProp(
|
hostPatchProp(
|
||||||
el,
|
el,
|
||||||
key,
|
key,
|
||||||
next,
|
next,
|
||||||
prev,
|
prev,
|
||||||
false,
|
false,
|
||||||
n1.children as VNode[],
|
n1.children as VNode[],
|
||||||
unmountChildren
|
unmountChildren
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,18 @@ export const STYLE = 1 << 2
|
|||||||
// them faster (without having to worry about removed props)
|
// them faster (without having to worry about removed props)
|
||||||
export const PROPS = 1 << 3
|
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
|
// 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
|
// 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
|
// Indicates a component with dynamic slots (e.g. slot that references a v-for
|
||||||
// iterated value). Components with this flag are always force updated.
|
// iterated value, or dynamic slot names).
|
||||||
export const SLOTS = 1 << 6
|
// Components with this flag are always force updated.
|
||||||
|
export const DYNAMIC_SLOTS = 1 << 7
|
||||||
|
Loading…
Reference in New Issue
Block a user