wip: shouldUpdateComponent logic for slots

This commit is contained in:
Evan You 2019-05-31 12:25:11 +08:00
parent 333ceaa4b5
commit e0a66d0381
3 changed files with 87 additions and 56 deletions

View File

@ -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,6 +218,12 @@ export function shouldUpdateComponent(
if (nextProps === null) { if (nextProps === null) {
return prevProps !== null return prevProps !== null
} }
return hasPropsChanged(prevProps, nextProps)
}
return false
}
function hasPropsChanged(prevProps: Data, nextProps: Data): boolean {
const nextKeys = Object.keys(nextProps) const nextKeys = Object.keys(nextProps)
if (nextKeys.length !== Object.keys(prevProps).length) { if (nextKeys.length !== Object.keys(prevProps).length) {
return true return true
@ -221,6 +234,5 @@ export function shouldUpdateComponent(
return true return true
} }
} }
}
return false return false
} }

View File

@ -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,6 +231,10 @@ 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)
if (patchFlag & FULL_PROPS) {
// element props contain dynamic keys, full diff needed
patchProps(el, n2, oldProps, newProps)
} else {
// class // class
// this flag is matched when the element has dynamic class bindings. // this flag is matched when the element has dynamic class bindings.
if (patchFlag & CLASS) { if (patchFlag & CLASS) {
@ -265,6 +277,7 @@ export function createRenderer(options: RendererOptions) {
} }
} }
} }
}
// text // text
// This flag is matched when the element has only dynamic text children. // This flag is matched when the element has only dynamic text children.

View File

@ -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