feat: detailed info in renderTriggered + hint for skipping slot updates

This commit is contained in:
Evan You 2018-11-08 18:20:07 -05:00
parent 6027d480f3
commit 64029b4a54
3 changed files with 50 additions and 5 deletions

View File

@ -204,9 +204,7 @@ export function shouldUpdateComponent(
const { data: prevProps, childFlags: prevChildFlags } = prevVNode const { data: prevProps, childFlags: prevChildFlags } = prevVNode
const { data: nextProps, childFlags: nextChildFlags } = nextVNode const { data: nextProps, childFlags: nextChildFlags } = nextVNode
// If has different slots content, or has non-compiled slots, // If has different slots content, or has non-compiled slots,
// the child needs to be force updated. It's ok to call $forceUpdate // the child needs to be force updated.
// again even if props update has already queued an update, as the
// scheduler will not queue the same update twice.
if ( if (
prevChildFlags !== nextChildFlags || prevChildFlags !== nextChildFlags ||
(nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0 (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
@ -235,11 +233,49 @@ export function shouldUpdateComponent(
return false return false
} }
// DEV only
export function getReasonForComponentUpdate( export function getReasonForComponentUpdate(
prevVNode: VNode, prevVNode: VNode,
nextVNode: VNode nextVNode: VNode
): any { ): any {
// TODO: extract more detailed information on why the component is updating const reasons = []
const { childFlags: prevChildFlags } = prevVNode
const { childFlags: nextChildFlags } = nextVNode
if (
prevChildFlags !== nextChildFlags ||
(nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
) {
reasons.push({
type: `slots may have changed`,
tip: `use function slots + $stable: true to avoid slot-triggered child updates.`
})
}
const prevProps = prevVNode.data || EMPTY_OBJ
const nextProps = nextVNode.data || EMPTY_OBJ
for (const key in nextProps) {
if (nextProps[key] !== prevProps[key]) {
reasons.push({
type: 'prop changed',
key,
value: nextProps[key],
oldValue: prevProps[key]
})
}
}
for (const key in prevProps) {
if (!(key in nextProps)) {
reasons.push({
type: 'prop changed',
key,
value: undefined,
oldValue: prevProps[key]
})
}
}
return {
type: 'triggered by parent',
reasons
}
} }
export function createComponentClassFromOptions( export function createComponentClassFromOptions(

View File

@ -22,7 +22,8 @@ export const Portal = Symbol()
type RawChildType = VNode | string | number | boolean | null | undefined type RawChildType = VNode | string | number | boolean | null | undefined
export type RawSlots = { export type RawSlots = {
[name: string]: () => RawChildrenType $stable?: boolean
[name: string]: RawChildType | (() => RawChildrenType)
} }
export type RawChildrenType = RawChildType | RawChildType[] export type RawChildrenType = RawChildType | RawChildType[]

View File

@ -10,6 +10,7 @@ import { RawChildrenType, RawSlots } from './h'
import { FunctionalHandle } from './createRenderer' import { FunctionalHandle } from './createRenderer'
const handlersRE = /^on|^vnode/ const handlersRE = /^on|^vnode/
const STABLE_SLOTS_HINT = '$stable'
// Vue core is platform agnostic, so we are not using Element for "DOM" nodes. // Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
export interface RenderNode { export interface RenderNode {
@ -226,6 +227,10 @@ export function createComponentVNode(
} else if (isObject(children) && !(children as any)._isVNode) { } else if (isObject(children) && !(children as any)._isVNode) {
// slot object as children // slot object as children
slots = children slots = children
// special manual optimization hint for raw render fn users
if (slots[STABLE_SLOTS_HINT]) {
childFlags = ChildrenFlags.STABLE_SLOTS
}
} else { } else {
slots = { default: () => children } slots = { default: () => children }
} }
@ -418,6 +423,9 @@ function normalizeSlots(slots: { [name: string]: any }): Slots {
} }
const normalized = { _normalized: true } as any const normalized = { _normalized: true } as any
for (const name in slots) { for (const name in slots) {
if (name === STABLE_SLOTS_HINT) {
continue
}
normalized[name] = (...args: any[]) => normalizeSlot(slots[name](...args)) normalized[name] = (...args: any[]) => normalizeSlot(slots[name](...args))
} }
return normalized return normalized