parent
ba3b3cdda9
commit
65beba98fe
@ -41,7 +41,7 @@ const _withId = /*#__PURE__*/_withScopeId(\\"test\\")
|
||||
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\\",
|
||||
|
@ -9,7 +9,7 @@ return function render(_ctx, _cache) {
|
||||
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 */))
|
||||
}"
|
||||
`;
|
||||
@ -35,7 +35,7 @@ exports[`compiler: transform component slots named slot with v-for w/ prefixIden
|
||||
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,
|
||||
@ -52,7 +52,7 @@ exports[`compiler: transform component slots named slot with v-if + prefixIdenti
|
||||
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\\",
|
||||
@ -72,7 +72,7 @@ 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 }, [
|
||||
ok
|
||||
? {
|
||||
name: \\"one\\",
|
||||
@ -101,7 +101,7 @@ 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 }, [
|
||||
ok
|
||||
? {
|
||||
name: \\"one\\",
|
||||
@ -145,7 +145,7 @@ return function render(_ctx, _cache) {
|
||||
default: _withCtx(({ foo }) => [
|
||||
_createVNode(_component_Inner, null, {
|
||||
default: _withCtx(({ bar }) => [_toDisplayString(foo), _toDisplayString(bar), _toDisplayString(_ctx.baz)]),
|
||||
_: 1
|
||||
_: 2
|
||||
}, 1024 /* DYNAMIC_SLOTS */),
|
||||
\\" \\",
|
||||
_toDisplayString(foo),
|
||||
@ -165,8 +165,8 @@ return function render(_ctx, _cache) {
|
||||
|
||||
return (_openBlock(), _createBlock(_component_Comp, null, {
|
||||
[_ctx.named]: _withCtx(({ foo }) => [_toDisplayString(foo), _toDisplayString(_ctx.bar)]),
|
||||
_: 1
|
||||
}))
|
||||
_: 2
|
||||
}, 1024 /* DYNAMIC_SLOTS */))
|
||||
}"
|
||||
`;
|
||||
|
||||
|
@ -53,7 +53,7 @@ function parseWithSlots(template: string, options: CompilerOptions = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function createSlotMatcher(obj: Record<string, any>) {
|
||||
function createSlotMatcher(obj: Record<string, any>, isDynamic = false) {
|
||||
return {
|
||||
type: NodeTypes.JS_OBJECT_EXPRESSION,
|
||||
properties: Object.keys(obj)
|
||||
@ -70,7 +70,7 @@ function createSlotMatcher(obj: Record<string, any>) {
|
||||
})
|
||||
.concat({
|
||||
key: { content: `_` },
|
||||
value: { content: `1`, isStatic: false }
|
||||
value: { content: isDynamic ? `2` : `1`, isStatic: false }
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -230,29 +230,32 @@ describe('compiler: transform component slots', () => {
|
||||
{ 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()
|
||||
})
|
||||
@ -307,50 +310,53 @@ describe('compiler: transform component slots', () => {
|
||||
{ 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`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `bar`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
'[_ctx.two]': {
|
||||
type: NodeTypes.JS_FUNCTION_EXPRESSION,
|
||||
params: {
|
||||
type: NodeTypes.COMPOUND_EXPRESSION,
|
||||
children: [`{ `, { content: `bar` }, ` }`]
|
||||
},
|
||||
returns: [
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.foo`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `bar`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
true
|
||||
)
|
||||
)
|
||||
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
|
||||
})
|
||||
@ -382,35 +388,38 @@ describe('compiler: transform component slots', () => {
|
||||
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`
|
||||
}
|
||||
children: createSlotMatcher(
|
||||
{
|
||||
default: {
|
||||
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: `foo`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `bar`
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.baz`
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
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)
|
||||
@ -522,7 +531,7 @@ describe('compiler: transform component slots', () => {
|
||||
callee: CREATE_SLOTS,
|
||||
arguments: [
|
||||
createObjectMatcher({
|
||||
_: `[1]`
|
||||
_: `[2]`
|
||||
}),
|
||||
{
|
||||
type: NodeTypes.JS_ARRAY_EXPRESSION,
|
||||
@ -564,7 +573,7 @@ describe('compiler: transform component slots', () => {
|
||||
callee: CREATE_SLOTS,
|
||||
arguments: [
|
||||
createObjectMatcher({
|
||||
_: `[1]`
|
||||
_: `[2]`
|
||||
}),
|
||||
{
|
||||
type: NodeTypes.JS_ARRAY_EXPRESSION,
|
||||
@ -613,7 +622,7 @@ describe('compiler: transform component slots', () => {
|
||||
callee: CREATE_SLOTS,
|
||||
arguments: [
|
||||
createObjectMatcher({
|
||||
_: `[1]`
|
||||
_: `[2]`
|
||||
}),
|
||||
{
|
||||
type: NodeTypes.JS_ARRAY_EXPRESSION,
|
||||
@ -672,7 +681,7 @@ describe('compiler: transform component slots', () => {
|
||||
callee: CREATE_SLOTS,
|
||||
arguments: [
|
||||
createObjectMatcher({
|
||||
_: `[1]`
|
||||
_: `[2]`
|
||||
}),
|
||||
{
|
||||
type: NodeTypes.JS_ARRAY_EXPRESSION,
|
||||
|
@ -135,7 +135,7 @@ export function buildSlots(
|
||||
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)
|
||||
}
|
||||
|
||||
@ -144,6 +144,9 @@ export function buildSlots(
|
||||
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),
|
||||
@ -317,7 +320,12 @@ export function buildSlots(
|
||||
|
||||
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
|
||||
|
@ -140,7 +140,7 @@ describe('ssr: components', () => {
|
||||
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\\",
|
||||
@ -172,7 +172,7 @@ describe('ssr: components', () => {
|
||||
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,
|
||||
|
@ -11,7 +11,6 @@ import {
|
||||
isFunction,
|
||||
EMPTY_OBJ,
|
||||
ShapeFlags,
|
||||
PatchFlags,
|
||||
extend,
|
||||
def
|
||||
} from '@vue/shared'
|
||||
@ -28,6 +27,11 @@ export type InternalSlots = {
|
||||
|
||||
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
|
||||
@ -40,7 +44,7 @@ export type RawSlots = {
|
||||
// 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'
|
||||
@ -105,10 +109,11 @@ export const initSlots = (
|
||||
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 = {}))
|
||||
}
|
||||
@ -129,21 +134,20 @@ export const updateSlots = (
|
||||
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 {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Data } from '../component'
|
||||
import { Slots } from '../componentSlots'
|
||||
import { Slots, RawSlots, CompiledSlotTypes } from '../componentSlots'
|
||||
import {
|
||||
VNodeArrayChildren,
|
||||
openBlock,
|
||||
@ -39,7 +39,9 @@ export function renderSlot(
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user