export const render = /*#__PURE__*/_withId(function render(_ctx, _cache) {
const _component_Child = _resolveComponent(\\"Child\\")
- return (_openBlock(), _createBlock(_component_Child, null, _createSlots({ _: 1 }, [
+ return (_openBlock(), _createBlock(_component_Child, null, _createSlots({ _: 2 }, [
(_ctx.ok)
? {
name: \\"foo\\",
return (_openBlock(), _createBlock(_component_Comp, null, {
[_ctx.one]: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
[_ctx.two]: _withCtx(({ bar }) => [_toDisplayString(_ctx.foo), _toDisplayString(bar)]),
- _: 1
+ _: 2
}, 1024 /* DYNAMIC_SLOTS */))
}"
`;
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
- return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 1 }, [
+ return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 2 }, [
_renderList(_ctx.list, (name) => {
return {
name: name,
return function render(_ctx, _cache) {
const _component_Comp = _resolveComponent(\\"Comp\\")
- return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 1 }, [
+ return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 2 }, [
(_ctx.ok)
? {
name: \\"one\\",
const _component_Comp = _resolveComponent(\\"Comp\\")
- return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 1 }, [
+ return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 2 }, [
ok
? {
name: \\"one\\",
const _component_Comp = _resolveComponent(\\"Comp\\")
- return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 1 }, [
+ return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 2 }, [
ok
? {
name: \\"one\\",
default: _withCtx(({ foo }) => [
_createVNode(_component_Inner, null, {
default: _withCtx(({ bar }) => [_toDisplayString(foo), _toDisplayString(bar), _toDisplayString(_ctx.baz)]),
- _: 1
+ _: 2
}, 1024 /* DYNAMIC_SLOTS */),
\\" \\",
_toDisplayString(foo),
return (_openBlock(), _createBlock(_component_Comp, null, {
[_ctx.named]: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
- _: 1
- }))
+ _: 2
+ }, 1024 /* DYNAMIC_SLOTS */))
}"
`;
}
}
-function createSlotMatcher(obj: Record<string, any>) {
+function createSlotMatcher(obj: Record<string, any>, isDynamic = false) {
return {
type: NodeTypes.JS_OBJECT_EXPRESSION,
properties: Object.keys(obj)
})
.concat({
key: { content: `_` },
- value: { content: `1`, isStatic: false }
+ value: { content: isDynamic ? `2` : `1`, isStatic: false }
})
}
}
{ prefixIdentifiers: true }
)
expect(slots).toMatchObject(
- createSlotMatcher({
- '[_ctx.named]': {
- type: NodeTypes.JS_FUNCTION_EXPRESSION,
- params: {
- type: NodeTypes.COMPOUND_EXPRESSION,
- children: [`{ `, { content: `foo` }, ` }`]
- },
- returns: [
- {
- type: NodeTypes.INTERPOLATION,
- content: {
- content: `foo`
- }
+ createSlotMatcher(
+ {
+ '[_ctx.named]': {
+ type: NodeTypes.JS_FUNCTION_EXPRESSION,
+ params: {
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [`{ `, { content: `foo` }, ` }`]
},
- {
- type: NodeTypes.INTERPOLATION,
- content: {
- content: `_ctx.bar`
+ returns: [
+ {
+ type: NodeTypes.INTERPOLATION,
+ content: {
+ content: `foo`
+ }
+ },
+ {
+ type: NodeTypes.INTERPOLATION,
+ content: {
+ content: `_ctx.bar`
+ }
}
- }
- ]
- }
- })
+ ]
+ }
+ },
+ true
+ )
)
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
{ prefixIdentifiers: true }
)
expect(slots).toMatchObject(
- createSlotMatcher({
- '[_ctx.one]': {
- type: NodeTypes.JS_FUNCTION_EXPRESSION,
- params: {
- type: NodeTypes.COMPOUND_EXPRESSION,
- children: [`{ `, { content: `foo` }, ` }`]
- },
- returns: [
- {
- type: NodeTypes.INTERPOLATION,
- content: {
- content: `foo`
- }
+ createSlotMatcher(
+ {
+ '[_ctx.one]': {
+ type: NodeTypes.JS_FUNCTION_EXPRESSION,
+ params: {
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [`{ `, { content: `foo` }, ` }`]
},
- {
- type: NodeTypes.INTERPOLATION,
- content: {
- content: `_ctx.bar`
+ returns: [
+ {
+ type: NodeTypes.INTERPOLATION,
+ content: {
+ content: `foo`
+ }
+ },
+ {
+ type: NodeTypes.INTERPOLATION,
+ content: {
+ content: `_ctx.bar`
+ }
}
- }
- ]
- },
- '[_ctx.two]': {
- type: NodeTypes.JS_FUNCTION_EXPRESSION,
- params: {
- type: NodeTypes.COMPOUND_EXPRESSION,
- children: [`{ `, { content: `bar` }, ` }`]
+ ]
},
- returns: [
- {
- type: NodeTypes.INTERPOLATION,
- content: {
- content: `_ctx.foo`
- }
+ '[_ctx.two]': {
+ type: NodeTypes.JS_FUNCTION_EXPRESSION,
+ params: {
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [`{ `, { content: `bar` }, ` }`]
},
- {
- type: NodeTypes.INTERPOLATION,
- content: {
- content: `bar`
+ returns: [
+ {
+ type: NodeTypes.INTERPOLATION,
+ content: {
+ content: `_ctx.foo`
+ }
+ },
+ {
+ type: NodeTypes.INTERPOLATION,
+ content: {
+ content: `bar`
+ }
}
- }
- ]
- }
- })
+ ]
+ }
+ },
+ true
+ )
)
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
type: NodeTypes.VNODE_CALL,
tag: `_component_Inner`,
props: undefined,
- children: createSlotMatcher({
- default: {
- type: NodeTypes.JS_FUNCTION_EXPRESSION,
- params: {
- type: NodeTypes.COMPOUND_EXPRESSION,
- children: [`{ `, { content: `bar` }, ` }`]
- },
- returns: [
- {
- type: NodeTypes.INTERPOLATION,
- content: {
- content: `foo`
- }
- },
- {
- type: NodeTypes.INTERPOLATION,
- content: {
- content: `bar`
- }
+ children: createSlotMatcher(
+ {
+ default: {
+ type: NodeTypes.JS_FUNCTION_EXPRESSION,
+ params: {
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [`{ `, { content: `bar` }, ` }`]
},
- {
- type: NodeTypes.INTERPOLATION,
- content: {
- content: `_ctx.baz`
+ returns: [
+ {
+ type: NodeTypes.INTERPOLATION,
+ content: {
+ content: `foo`
+ }
+ },
+ {
+ type: NodeTypes.INTERPOLATION,
+ content: {
+ content: `bar`
+ }
+ },
+ {
+ type: NodeTypes.INTERPOLATION,
+ content: {
+ content: `_ctx.baz`
+ }
}
- }
- ]
- }
- }),
+ ]
+ }
+ },
+ true
+ ),
// nested slot should be forced dynamic, since scope variables
// are not tracked as dependencies of the slot.
patchFlag: genFlagText(PatchFlags.DYNAMIC_SLOTS)
callee: CREATE_SLOTS,
arguments: [
createObjectMatcher({
- _: `[1]`
+ _: `[2]`
}),
{
type: NodeTypes.JS_ARRAY_EXPRESSION,
callee: CREATE_SLOTS,
arguments: [
createObjectMatcher({
- _: `[1]`
+ _: `[2]`
}),
{
type: NodeTypes.JS_ARRAY_EXPRESSION,
callee: CREATE_SLOTS,
arguments: [
createObjectMatcher({
- _: `[1]`
+ _: `[2]`
}),
{
type: NodeTypes.JS_ARRAY_EXPRESSION,
callee: CREATE_SLOTS,
arguments: [
createObjectMatcher({
- _: `[1]`
+ _: `[2]`
}),
{
type: NodeTypes.JS_ARRAY_EXPRESSION,
let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0
// with `prefixIdentifiers: true`, this can be further optimized to make
// it dynamic only when the slot actually uses the scope variables.
- if (!__BROWSER__ && context.prefixIdentifiers) {
+ if (!__BROWSER__ && !context.ssr && context.prefixIdentifiers) {
hasDynamicSlots = hasScopeRef(node, context.identifiers)
}
const onComponentSlot = findDir(node, 'slot', true)
if (onComponentSlot) {
const { arg, exp } = onComponentSlot
+ if (arg && !isStaticExp(arg)) {
+ hasDynamicSlots = true
+ }
slotsProperties.push(
createObjectProperty(
arg || createSimpleExpression('default', true),
let slots = createObjectExpression(
slotsProperties.concat(
- createObjectProperty(`_`, createSimpleExpression(`1`, false))
+ createObjectProperty(
+ `_`,
+ // 2 = compiled but dynamic = can skip normalization, but must run diff
+ // 1 = compiled and static = can skip normalization AND diff as optimized
+ createSimpleExpression(hasDynamicSlots ? `2` : `1`, false)
+ )
),
loc
) as SlotsExpression
return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, _attrs, _createSlots({ _: 1 }, [
+ _push(_ssrRenderComponent(_component_foo, _attrs, _createSlots({ _: 2 }, [
(_ctx.ok)
? {
name: \\"named\\",
return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, _attrs, _createSlots({ _: 1 }, [
+ _push(_ssrRenderComponent(_component_foo, _attrs, _createSlots({ _: 2 }, [
_renderList(_ctx.names, (key) => {
return {
name: key,
isFunction,
EMPTY_OBJ,
ShapeFlags,
- PatchFlags,
extend,
def
} from '@vue/shared'
export type Slots = Readonly<InternalSlots>
+export const enum CompiledSlotTypes {
+ STATIC = 1,
+ DYNAMIC = 2
+}
+
export type RawSlots = {
[name: string]: unknown
// manual render fn hint to skip forced children updates
// object may be directly passed down to a child component in a manual
// render function, and the optimization hint need to be on the slot object
// itself to be preserved.
- _?: 1
+ _?: CompiledSlotTypes
}
const isInternalKey = (key: string) => key[0] === '_' || key === '$stable'
children: VNodeNormalizedChildren
) => {
if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
- if ((children as RawSlots)._ === 1) {
+ const type = (children as RawSlots)._
+ if (type) {
instance.slots = children as InternalSlots
// make compiler marker non-enumerable
- def(children as InternalSlots, '_', 1)
+ def(children as InternalSlots, '_', type)
} else {
normalizeObjectSlots(children as RawSlots, (instance.slots = {}))
}
let needDeletionCheck = true
let deletionComparisonTarget = EMPTY_OBJ
if (vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
- if ((children as RawSlots)._ === 1) {
+ const type = (children as RawSlots)._
+ if (type) {
// compiled slots.
if (__DEV__ && isHmrUpdating) {
// Parent was HMR updated so slot content may have changed.
// force update slots and mark instance for hmr as well
extend(slots, children as Slots)
- } else if (
- // bail on dynamic slots (v-if, v-for, reference of scope variables)
- !(vnode.patchFlag & PatchFlags.DYNAMIC_SLOTS)
- ) {
+ } else if (type === CompiledSlotTypes.STATIC) {
// compiled AND static.
// no need to update, and skip stale slots removal.
needDeletionCheck = false
} else {
- // compiled but dynamic - update slots, but skip normalization.
+ // compiled but dynamic (v-if/v-for on slots) - update slots, but skip
+ // normalization.
extend(slots, children as Slots)
}
} else {
import { Data } from '../component'
-import { Slots } from '../componentSlots'
+import { Slots, RawSlots, CompiledSlotTypes } from '../componentSlots'
import {
VNodeArrayChildren,
openBlock,
Fragment,
{ key: props.key },
slot ? slot(props) : fallback ? fallback() : [],
- slots._ ? PatchFlags.STABLE_FRAGMENT : PatchFlags.BAIL
+ (slots as RawSlots)._ === CompiledSlotTypes.STATIC
+ ? PatchFlags.STABLE_FRAGMENT
+ : PatchFlags.BAIL
)
)
}