Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: improve scoped slots change detection accuracy #9371

Merged
merged 1 commit into from
Jan 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions flow/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ declare type ASTElement = {
transitionMode?: string | null;
slotName?: ?string;
slotTarget?: ?string;
slotTargetDynamic?: boolean;
slotScope?: ?string;
scopedSlots?: { [name: string]: ASTElement };

Expand Down
3 changes: 2 additions & 1 deletion src/compiler/codegen/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,11 +354,12 @@ function genScopedSlots (
slots: { [key: string]: ASTElement },
state: CodegenState
): string {
const hasDynamicKeys = Object.keys(slots).some(key => slots[key].slotTargetDynamic)
return `scopedSlots:_u([${
Object.keys(slots).map(key => {
return genScopedSlot(key, slots[key], state)
}).join(',')
}])`
}]${hasDynamicKeys ? `,true` : ``})`
}

function genScopedSlot (
Expand Down
18 changes: 11 additions & 7 deletions src/compiler/parser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ function processSlotContent (el) {
const slotTarget = getBindingAttr(el, 'slot')
if (slotTarget) {
el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
el.slotTargetDynamic = !!(el.attrsMap[':slot'] || el.attrsMap['v-bind:slot'])
// preserve slot as an attribute for native shadow DOM compat
// only for non-scoped slots.
if (el.tag !== 'template' && !el.slotScope) {
Expand All @@ -607,8 +608,10 @@ function processSlotContent (el) {
)
}
}
el.slotTarget = getSlotName(slotBinding)
el.slotScope = slotBinding.value
const { name, dynamic } = getSlotName(slotBinding)
el.slotTarget = name
el.slotTargetDynamic = dynamic
el.slotScope = slotBinding.value || `_` // force it into a scoped slot for perf
}
} else {
// v-slot on component, denotes default slot
Expand Down Expand Up @@ -637,10 +640,11 @@ function processSlotContent (el) {
}
// add the component's children to its default slot
const slots = el.scopedSlots || (el.scopedSlots = {})
const target = getSlotName(slotBinding)
const slotContainer = slots[target] = createASTElement('template', [], el)
const { name, dynamic } = getSlotName(slotBinding)
const slotContainer = slots[name] = createASTElement('template', [], el)
slotContainer.slotTargetDynamic = dynamic
slotContainer.children = el.children
slotContainer.slotScope = slotBinding.value
slotContainer.slotScope = slotBinding.value || `_`
// remove children as they are returned from scopedSlots now
el.children = []
// mark el non-plain so data gets generated
Expand All @@ -664,9 +668,9 @@ function getSlotName (binding) {
}
return dynamicKeyRE.test(name)
// dynamic [name]
? name.slice(1, -1)
? { name: name.slice(1, -1), dynamic: true }
// static name
: `"${name}"`
: { name: `"${name}"`, dynamic: false }
}

// handle <slot/> outlets
Expand Down
20 changes: 15 additions & 5 deletions src/core/instance/lifecycle.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,22 @@ export function updateChildComponent (
}

// determine whether component has slot children
// we need to do this before overwriting $options._renderChildren
const hasChildren = !!(
// we need to do this before overwriting $options._renderChildren.

// check if there are dynamic scopedSlots (hand-written or compiled but with
// dynamic slot names). Static scoped slots compiled from template has the
// "$stable" marker.
const hasDynamicScopedSlot = !!(
(parentVnode.data.scopedSlots && !parentVnode.data.scopedSlots.$stable) ||
(vm.$scopedSlots !== emptyObject && !vm.$scopedSlots.$stable)
)
// Any static slot children from the parent may have changed during parent's
// update. Dynamic scoped slots may also have changed. In such cases, a forced
// update is necessary to ensure correctness.
const needsForceUpdate = !!(
renderChildren || // has new static slots
vm.$options._renderChildren || // has old static slots
parentVnode.data.scopedSlots || // has new scoped slots
vm.$scopedSlots !== emptyObject // has old scoped slots
hasDynamicScopedSlot
)

vm.$options._parentVnode = parentVnode
Expand Down Expand Up @@ -268,7 +278,7 @@ export function updateChildComponent (
updateComponentListeners(vm, listeners, oldListeners)

// resolve slots + force update if has children
if (hasChildren) {
if (needsForceUpdate) {
vm.$slots = resolveSlots(renderChildren, parentVnode.context)
vm.$forceUpdate()
}
Expand Down
7 changes: 4 additions & 3 deletions src/core/instance/render-helpers/resolve-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ function isWhitespace (node: VNode): boolean {

export function resolveScopedSlots (
fns: ScopedSlotsData, // see flow/vnode
hasDynamicKeys?: boolean,
res?: Object
): { [key: string]: Function } {
res = res || {}
): { [key: string]: Function, $stable: boolean } {
res = res || { $stable: !hasDynamicKeys }
for (let i = 0; i < fns.length; i++) {
const slot = fns[i]
if (Array.isArray(slot)) {
resolveScopedSlots(slot, res)
resolveScopedSlots(slot, hasDynamicKeys, res)
} else {
res[slot.key] = slot.fn
}
Expand Down
3 changes: 2 additions & 1 deletion src/core/vdom/helpers/normalize-scoped-slots.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export function normalizeScopedSlots (
} else {
res = {}
for (const key in slots) {
if (slots[key]) {
if (slots[key] && key[0] !== '$') {
res[key] = normalizeScopedSlot(slots[key])
}
}
Expand All @@ -26,6 +26,7 @@ export function normalizeScopedSlots (
}
}
res._normalized = true
res.$stable = slots && slots.$stable
return res
}

Expand Down
Loading