Skip to content

Commit 7f93c76

Browse files
committed
wip: exclude legacy slots from $scopedSlots
1 parent b14de6c commit 7f93c76

File tree

7 files changed

+80
-28
lines changed

7 files changed

+80
-28
lines changed

packages/compiler-core/src/ast.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,11 @@ export interface FunctionExpression extends Node {
352352
* withScopeId() wrapper
353353
*/
354354
isSlot: boolean
355+
/**
356+
* __COMPAT__ only, indicates a slot function that should be excluded from
357+
* the legacy $scopedSlots instance property.
358+
*/
359+
isNonScopedSlot?: boolean
355360
}
356361

357362
export interface ConditionalExpression extends Node {

packages/compiler-core/src/codegen.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,9 @@ function genFunctionExpression(
878878
push(`}`)
879879
}
880880
if (isSlot) {
881+
if (__COMPAT__ && node.isNonScopedSlot) {
882+
push(`, undefined, true`)
883+
}
881884
push(`)`)
882885
}
883886
}

packages/compiler-core/src/transforms/vSlot.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,6 @@ export function buildSlots(
129129
const slotsProperties: Property[] = []
130130
const dynamicSlots: (ConditionalExpression | CallExpression)[] = []
131131

132-
const buildDefaultSlotProperty = (
133-
props: ExpressionNode | undefined,
134-
children: TemplateChildNode[]
135-
) => createObjectProperty(`default`, buildSlotFn(props, children, loc))
136-
137132
// If the slot is inside a v-for or another v-slot, force it to be dynamic
138133
// since it likely uses a scope variable.
139134
let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0
@@ -302,6 +297,17 @@ export function buildSlots(
302297
}
303298

304299
if (!onComponentSlot) {
300+
const buildDefaultSlotProperty = (
301+
props: ExpressionNode | undefined,
302+
children: TemplateChildNode[]
303+
) => {
304+
const fn = buildSlotFn(props, children, loc)
305+
if (__COMPAT__) {
306+
fn.isNonScopedSlot = true
307+
}
308+
return createObjectProperty(`default`, fn)
309+
}
310+
305311
if (!hasTemplateSlots) {
306312
// implicit default slot (on component)
307313
slotsProperties.push(buildDefaultSlotProperty(undefined, children))

packages/runtime-core/src/compat/instance.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {
3636
} from './renderHelpers'
3737
import { resolveFilter } from '../helpers/resolveAssets'
3838
import { resolveMergedOptions } from '../componentOptions'
39-
import { Slots } from '../componentSlots'
39+
import { InternalSlots, Slots } from '../componentSlots'
4040

4141
export type LegacyPublicInstance = ComponentPublicInstance &
4242
LegacyPublicProperties
@@ -103,7 +103,14 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
103103

104104
$scopedSlots: i => {
105105
assertCompatEnabled(DeprecationTypes.INSTANCE_SCOPED_SLOTS, i)
106-
return __DEV__ ? shallowReadonly(i.slots) : i.slots
106+
const res: InternalSlots = {}
107+
for (const key in i.slots) {
108+
const fn = i.slots[key]!
109+
if (!(fn as any)._nonScoped) {
110+
res[key] = fn
111+
}
112+
}
113+
return res
107114
},
108115

109116
$on: i => on.bind(null, i),

packages/runtime-core/src/compat/renderFn.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ function convertLegacySlots(vnode: VNode): VNode {
281281
for (const key in slots) {
282282
const slotChildren = slots[key]
283283
slots[key] = () => slotChildren
284+
slots[key]._nonScoped = true
284285
}
285286
}
286287
}

packages/runtime-core/src/componentRenderContext.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ export const withScopeId = (_id: string) => withCtx
6161
*/
6262
export function withCtx(
6363
fn: Function,
64-
ctx: ComponentInternalInstance | null = currentRenderingInstance
64+
ctx: ComponentInternalInstance | null = currentRenderingInstance,
65+
isNonScopedSlot?: boolean // __COMPAT__ only
6566
) {
6667
if (!ctx) return fn
6768
const renderFnWithContext = (...args: any[]) => {
@@ -83,5 +84,8 @@ export function withCtx(
8384
// this is used in vnode.ts -> normalizeChildren() to set the slot
8485
// rendering flag.
8586
renderFnWithContext._c = true
87+
if (__COMPAT__ && isNonScopedSlot) {
88+
renderFnWithContext._nonScoped = true
89+
}
8690
return renderFnWithContext
8791
}

packages/vue-compat/__tests__/instance.spec.ts

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -251,30 +251,56 @@ test('INSTANCE_LISTENERS', () => {
251251
).toHaveBeenWarned()
252252
})
253253

254-
test('INSTANCE_SCOPED_SLOTS', () => {
255-
let slots: Slots
256-
new Vue({
257-
template: `<child v-slot="{ msg }">{{ msg }}</child>`,
258-
components: {
259-
child: {
260-
compatConfig: { RENDER_FUNCTION: false },
261-
render() {
262-
slots = this.$scopedSlots
254+
describe('INSTANCE_SCOPED_SLOTS', () => {
255+
test('explicit usage', () => {
256+
let slots: Slots
257+
new Vue({
258+
template: `<child v-slot="{ msg }">{{ msg }}</child>`,
259+
components: {
260+
child: {
261+
compatConfig: { RENDER_FUNCTION: false },
262+
render() {
263+
slots = this.$scopedSlots
264+
}
263265
}
264266
}
265-
}
266-
}).$mount()
267+
}).$mount()
267268

268-
expect(slots!.default!({ msg: 'hi' })).toMatchObject([
269-
{
270-
type: Text,
271-
children: 'hi'
272-
}
273-
])
269+
expect(slots!.default!({ msg: 'hi' })).toMatchObject([
270+
{
271+
type: Text,
272+
children: 'hi'
273+
}
274+
])
274275

275-
expect(
276-
deprecationData[DeprecationTypes.INSTANCE_SCOPED_SLOTS].message
277-
).toHaveBeenWarned()
276+
expect(
277+
deprecationData[DeprecationTypes.INSTANCE_SCOPED_SLOTS].message
278+
).toHaveBeenWarned()
279+
})
280+
281+
test('should not include legacy slot usage in $scopedSlots', () => {
282+
let normalSlots: Slots
283+
let scopedSlots: Slots
284+
new Vue({
285+
template: `<child><div>default</div></child>`,
286+
components: {
287+
child: {
288+
compatConfig: { RENDER_FUNCTION: false },
289+
render() {
290+
normalSlots = this.$slots
291+
scopedSlots = this.$scopedSlots
292+
}
293+
}
294+
}
295+
}).$mount()
296+
297+
expect('default' in normalSlots!).toBe(true)
298+
expect('default' in scopedSlots!).toBe(false)
299+
300+
expect(
301+
deprecationData[DeprecationTypes.INSTANCE_SCOPED_SLOTS].message
302+
).toHaveBeenWarned()
303+
})
278304
})
279305

280306
test('INSTANCE_ATTR_CLASS_STYLE', () => {

0 commit comments

Comments
 (0)