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