wip: exclude legacy slots from $scopedSlots
This commit is contained in:
parent
b14de6c3f8
commit
7f93c76b96
@ -352,6 +352,11 @@ export interface FunctionExpression extends Node {
|
||||
* withScopeId() wrapper
|
||||
*/
|
||||
isSlot: boolean
|
||||
/**
|
||||
* __COMPAT__ only, indicates a slot function that should be excluded from
|
||||
* the legacy $scopedSlots instance property.
|
||||
*/
|
||||
isNonScopedSlot?: boolean
|
||||
}
|
||||
|
||||
export interface ConditionalExpression extends Node {
|
||||
|
@ -878,6 +878,9 @@ function genFunctionExpression(
|
||||
push(`}`)
|
||||
}
|
||||
if (isSlot) {
|
||||
if (__COMPAT__ && node.isNonScopedSlot) {
|
||||
push(`, undefined, true`)
|
||||
}
|
||||
push(`)`)
|
||||
}
|
||||
}
|
||||
|
@ -129,11 +129,6 @@ export function buildSlots(
|
||||
const slotsProperties: Property[] = []
|
||||
const dynamicSlots: (ConditionalExpression | CallExpression)[] = []
|
||||
|
||||
const buildDefaultSlotProperty = (
|
||||
props: ExpressionNode | undefined,
|
||||
children: TemplateChildNode[]
|
||||
) => createObjectProperty(`default`, buildSlotFn(props, children, loc))
|
||||
|
||||
// If the slot is inside a v-for or another v-slot, force it to be dynamic
|
||||
// since it likely uses a scope variable.
|
||||
let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0
|
||||
@ -302,6 +297,17 @@ export function buildSlots(
|
||||
}
|
||||
|
||||
if (!onComponentSlot) {
|
||||
const buildDefaultSlotProperty = (
|
||||
props: ExpressionNode | undefined,
|
||||
children: TemplateChildNode[]
|
||||
) => {
|
||||
const fn = buildSlotFn(props, children, loc)
|
||||
if (__COMPAT__) {
|
||||
fn.isNonScopedSlot = true
|
||||
}
|
||||
return createObjectProperty(`default`, fn)
|
||||
}
|
||||
|
||||
if (!hasTemplateSlots) {
|
||||
// implicit default slot (on component)
|
||||
slotsProperties.push(buildDefaultSlotProperty(undefined, children))
|
||||
|
@ -36,7 +36,7 @@ import {
|
||||
} from './renderHelpers'
|
||||
import { resolveFilter } from '../helpers/resolveAssets'
|
||||
import { resolveMergedOptions } from '../componentOptions'
|
||||
import { Slots } from '../componentSlots'
|
||||
import { InternalSlots, Slots } from '../componentSlots'
|
||||
|
||||
export type LegacyPublicInstance = ComponentPublicInstance &
|
||||
LegacyPublicProperties
|
||||
@ -103,7 +103,14 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
||||
|
||||
$scopedSlots: i => {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_SCOPED_SLOTS, i)
|
||||
return __DEV__ ? shallowReadonly(i.slots) : i.slots
|
||||
const res: InternalSlots = {}
|
||||
for (const key in i.slots) {
|
||||
const fn = i.slots[key]!
|
||||
if (!(fn as any)._nonScoped) {
|
||||
res[key] = fn
|
||||
}
|
||||
}
|
||||
return res
|
||||
},
|
||||
|
||||
$on: i => on.bind(null, i),
|
||||
|
@ -281,6 +281,7 @@ function convertLegacySlots(vnode: VNode): VNode {
|
||||
for (const key in slots) {
|
||||
const slotChildren = slots[key]
|
||||
slots[key] = () => slotChildren
|
||||
slots[key]._nonScoped = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,8 @@ export const withScopeId = (_id: string) => withCtx
|
||||
*/
|
||||
export function withCtx(
|
||||
fn: Function,
|
||||
ctx: ComponentInternalInstance | null = currentRenderingInstance
|
||||
ctx: ComponentInternalInstance | null = currentRenderingInstance,
|
||||
isNonScopedSlot?: boolean // __COMPAT__ only
|
||||
) {
|
||||
if (!ctx) return fn
|
||||
const renderFnWithContext = (...args: any[]) => {
|
||||
@ -83,5 +84,8 @@ export function withCtx(
|
||||
// this is used in vnode.ts -> normalizeChildren() to set the slot
|
||||
// rendering flag.
|
||||
renderFnWithContext._c = true
|
||||
if (__COMPAT__ && isNonScopedSlot) {
|
||||
renderFnWithContext._nonScoped = true
|
||||
}
|
||||
return renderFnWithContext
|
||||
}
|
||||
|
@ -251,30 +251,56 @@ test('INSTANCE_LISTENERS', () => {
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('INSTANCE_SCOPED_SLOTS', () => {
|
||||
let slots: Slots
|
||||
new Vue({
|
||||
template: `<child v-slot="{ msg }">{{ msg }}</child>`,
|
||||
components: {
|
||||
child: {
|
||||
compatConfig: { RENDER_FUNCTION: false },
|
||||
render() {
|
||||
slots = this.$scopedSlots
|
||||
describe('INSTANCE_SCOPED_SLOTS', () => {
|
||||
test('explicit usage', () => {
|
||||
let slots: Slots
|
||||
new Vue({
|
||||
template: `<child v-slot="{ msg }">{{ msg }}</child>`,
|
||||
components: {
|
||||
child: {
|
||||
compatConfig: { RENDER_FUNCTION: false },
|
||||
render() {
|
||||
slots = this.$scopedSlots
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).$mount()
|
||||
}).$mount()
|
||||
|
||||
expect(slots!.default!({ msg: 'hi' })).toMatchObject([
|
||||
{
|
||||
type: Text,
|
||||
children: 'hi'
|
||||
}
|
||||
])
|
||||
expect(slots!.default!({ msg: 'hi' })).toMatchObject([
|
||||
{
|
||||
type: Text,
|
||||
children: 'hi'
|
||||
}
|
||||
])
|
||||
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.INSTANCE_SCOPED_SLOTS].message
|
||||
).toHaveBeenWarned()
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.INSTANCE_SCOPED_SLOTS].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
|
||||
test('should not include legacy slot usage in $scopedSlots', () => {
|
||||
let normalSlots: Slots
|
||||
let scopedSlots: Slots
|
||||
new Vue({
|
||||
template: `<child><div>default</div></child>`,
|
||||
components: {
|
||||
child: {
|
||||
compatConfig: { RENDER_FUNCTION: false },
|
||||
render() {
|
||||
normalSlots = this.$slots
|
||||
scopedSlots = this.$scopedSlots
|
||||
}
|
||||
}
|
||||
}
|
||||
}).$mount()
|
||||
|
||||
expect('default' in normalSlots!).toBe(true)
|
||||
expect('default' in scopedSlots!).toBe(false)
|
||||
|
||||
expect(
|
||||
deprecationData[DeprecationTypes.INSTANCE_SCOPED_SLOTS].message
|
||||
).toHaveBeenWarned()
|
||||
})
|
||||
})
|
||||
|
||||
test('INSTANCE_ATTR_CLASS_STYLE', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user