mirror of https://github.com/vuejs/core.git
feat(runtime-vapor): attach current instance to render slot (#168)
This commit is contained in:
parent
db140a1e37
commit
9a33d79963
|
@ -34,21 +34,6 @@ function renderWithSlots(slots: any): any {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('component: slots', () => {
|
describe('component: slots', () => {
|
||||||
test('initSlots: instance.slots should be set correctly', () => {
|
|
||||||
const { slots } = renderWithSlots({ _: 1 })
|
|
||||||
expect(slots).toMatchObject({ _: 1 })
|
|
||||||
})
|
|
||||||
|
|
||||||
// NOTE: slot normalization is not supported
|
|
||||||
test.todo(
|
|
||||||
'initSlots: should normalize object slots (when value is null, string, array)',
|
|
||||||
() => {},
|
|
||||||
)
|
|
||||||
test.todo(
|
|
||||||
'initSlots: should normalize object slots (when value is function)',
|
|
||||||
() => {},
|
|
||||||
)
|
|
||||||
|
|
||||||
test('initSlots: instance.slots should be set correctly', () => {
|
test('initSlots: instance.slots should be set correctly', () => {
|
||||||
let instance: any
|
let instance: any
|
||||||
const Comp = defineComponent({
|
const Comp = defineComponent({
|
||||||
|
@ -73,6 +58,16 @@ describe('component: slots', () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// NOTE: slot normalization is not supported
|
||||||
|
test.todo(
|
||||||
|
'initSlots: should normalize object slots (when value is null, string, array)',
|
||||||
|
() => {},
|
||||||
|
)
|
||||||
|
test.todo(
|
||||||
|
'initSlots: should normalize object slots (when value is function)',
|
||||||
|
() => {},
|
||||||
|
)
|
||||||
|
|
||||||
// runtime-core's "initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)"
|
// runtime-core's "initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)"
|
||||||
test('initSlots: instance.slots should be set correctly', () => {
|
test('initSlots: instance.slots should be set correctly', () => {
|
||||||
const { slots } = renderWithSlots({
|
const { slots } = renderWithSlots({
|
||||||
|
@ -152,6 +147,60 @@ describe('component: slots', () => {
|
||||||
expect(instance.slots).toHaveProperty('footer')
|
expect(instance.slots).toHaveProperty('footer')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('the current instance should be kept in the slot', async () => {
|
||||||
|
let instanceInDefaultSlot: any
|
||||||
|
let instanceInVForSlot: any
|
||||||
|
let instanceInVIfSlot: any
|
||||||
|
|
||||||
|
const Comp = defineComponent({
|
||||||
|
render() {
|
||||||
|
const instance = getCurrentInstance()
|
||||||
|
instance!.slots.default!()
|
||||||
|
instance!.slots.inVFor!()
|
||||||
|
instance!.slots.inVIf!()
|
||||||
|
return template('<div></div>')()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const { instance } = define({
|
||||||
|
render() {
|
||||||
|
return createComponent(
|
||||||
|
Comp,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
default: () => {
|
||||||
|
instanceInDefaultSlot = getCurrentInstance()
|
||||||
|
return template('content')()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
() => [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
name: 'inVFor',
|
||||||
|
fn: () => {
|
||||||
|
instanceInVForSlot = getCurrentInstance()
|
||||||
|
return template('content')()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
name: 'inVIf',
|
||||||
|
key: '1',
|
||||||
|
fn: () => {
|
||||||
|
instanceInVIfSlot = getCurrentInstance()
|
||||||
|
return template('content')()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}).render()
|
||||||
|
|
||||||
|
expect(instanceInDefaultSlot).toBe(instance)
|
||||||
|
expect(instanceInVForSlot).toBe(instance)
|
||||||
|
expect(instanceInVIfSlot).toBe(instance)
|
||||||
|
})
|
||||||
|
|
||||||
test.todo('should respect $stable flag', async () => {
|
test.todo('should respect $stable flag', async () => {
|
||||||
// TODO: $stable flag?
|
// TODO: $stable flag?
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { type IfAny, extend, isArray } from '@vue/shared'
|
import { type IfAny, isArray } from '@vue/shared'
|
||||||
import { baseWatch } from '@vue/reactivity'
|
import { baseWatch } from '@vue/reactivity'
|
||||||
import type { ComponentInternalInstance } from './component'
|
import { type ComponentInternalInstance, setCurrentInstance } from './component'
|
||||||
import type { Block } from './apiRender'
|
import type { Block } from './apiRender'
|
||||||
import { createVaporPreScheduler } from './scheduler'
|
import { createVaporPreScheduler } from './scheduler'
|
||||||
|
|
||||||
|
@ -29,7 +29,14 @@ export const initSlots = (
|
||||||
rawSlots: InternalSlots | null = null,
|
rawSlots: InternalSlots | null = null,
|
||||||
dynamicSlots: DynamicSlots | null = null,
|
dynamicSlots: DynamicSlots | null = null,
|
||||||
) => {
|
) => {
|
||||||
const slots: InternalSlots = extend({}, rawSlots)
|
const slots: InternalSlots = {}
|
||||||
|
|
||||||
|
for (const key in rawSlots) {
|
||||||
|
const slot = rawSlots[key]
|
||||||
|
if (slot) {
|
||||||
|
slots[key] = withCtx(slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dynamicSlots) {
|
if (dynamicSlots) {
|
||||||
const dynamicSlotKeys: Record<string, true> = {}
|
const dynamicSlotKeys: Record<string, true> = {}
|
||||||
|
@ -41,12 +48,13 @@ export const initSlots = (
|
||||||
// array of dynamic slot generated by <template v-for="..." #[...]>
|
// array of dynamic slot generated by <template v-for="..." #[...]>
|
||||||
if (isArray(slot)) {
|
if (isArray(slot)) {
|
||||||
for (let j = 0; j < slot.length; j++) {
|
for (let j = 0; j < slot.length; j++) {
|
||||||
slots[slot[j].name] = slot[j].fn
|
slots[slot[j].name] = withCtx(slot[j].fn)
|
||||||
dynamicSlotKeys[slot[j].name] = true
|
dynamicSlotKeys[slot[j].name] = true
|
||||||
}
|
}
|
||||||
} else if (slot) {
|
} else if (slot) {
|
||||||
// conditional single slot generated by <template v-if="..." #foo>
|
// conditional single slot generated by <template v-if="..." #foo>
|
||||||
slots[slot.name] = slot.key
|
slots[slot.name] = withCtx(
|
||||||
|
slot.key
|
||||||
? (...args: any[]) => {
|
? (...args: any[]) => {
|
||||||
const res = slot.fn(...args)
|
const res = slot.fn(...args)
|
||||||
// attach branch key so each conditional branch is considered a
|
// attach branch key so each conditional branch is considered a
|
||||||
|
@ -54,7 +62,8 @@ export const initSlots = (
|
||||||
if (res) (res as any).key = slot.key
|
if (res) (res as any).key = slot.key
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
: slot.fn
|
: slot.fn,
|
||||||
|
)
|
||||||
dynamicSlotKeys[slot.name] = true
|
dynamicSlotKeys[slot.name] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,4 +86,15 @@ export const initSlots = (
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.slots = slots
|
instance.slots = slots
|
||||||
|
|
||||||
|
function withCtx(fn: Slot): Slot {
|
||||||
|
return (...args: any[]) => {
|
||||||
|
const reset = setCurrentInstance(instance.parent!)
|
||||||
|
try {
|
||||||
|
return fn(...args)
|
||||||
|
} finally {
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue