1- import { type IfAny , isArray } from '@vue/shared'
2- import { baseWatch } from '@vue/reactivity'
3- import { type ComponentInternalInstance , setCurrentInstance } from './component'
4- import type { Block } from './apiRender'
5- import { createVaporPreScheduler } from './scheduler'
1+ import { type IfAny , isArray , isFunction } from '@vue/shared'
2+ import {
3+ type EffectScope ,
4+ effectScope ,
5+ isReactive ,
6+ shallowReactive ,
7+ } from '@vue/reactivity'
8+ import {
9+ type ComponentInternalInstance ,
10+ currentInstance ,
11+ setCurrentInstance ,
12+ } from './component'
13+ import { type Block , type Fragment , fragmentKey } from './apiRender'
14+ import { renderEffect } from './renderEffect'
15+ import { createComment , createTextNode , insert , remove } from './dom/element'
616
717// TODO: SSR
818
@@ -29,7 +39,7 @@ export const initSlots = (
2939 rawSlots : InternalSlots | null = null ,
3040 dynamicSlots : DynamicSlots | null = null ,
3141) => {
32- const slots : InternalSlots = { }
42+ let slots : InternalSlots = { }
3343
3444 for ( const key in rawSlots ) {
3545 const slot = rawSlots [ key ]
@@ -39,50 +49,45 @@ export const initSlots = (
3949 }
4050
4151 if ( dynamicSlots ) {
52+ slots = shallowReactive ( slots )
4253 const dynamicSlotKeys : Record < string , true > = { }
43- baseWatch (
44- ( ) => {
45- const _dynamicSlots = dynamicSlots ( )
46- for ( let i = 0 ; i < _dynamicSlots . length ; i ++ ) {
47- const slot = _dynamicSlots [ i ]
48- // array of dynamic slot generated by <template v-for="..." #[...]>
49- if ( isArray ( slot ) ) {
50- for ( let j = 0 ; j < slot . length ; j ++ ) {
51- slots [ slot [ j ] . name ] = withCtx ( slot [ j ] . fn )
52- dynamicSlotKeys [ slot [ j ] . name ] = true
53- }
54- } else if ( slot ) {
55- // conditional single slot generated by <template v-if="..." #foo>
56- slots [ slot . name ] = withCtx (
57- slot . key
58- ? ( ...args : any [ ] ) => {
59- const res = slot . fn ( ...args )
60- // attach branch key so each conditional branch is considered a
61- // different fragment
62- if ( res ) ( res as any ) . key = slot . key
63- return res
64- }
65- : slot . fn ,
66- )
67- dynamicSlotKeys [ slot . name ] = true
54+ renderEffect ( ( ) => {
55+ const _dynamicSlots = dynamicSlots ( )
56+ for ( let i = 0 ; i < _dynamicSlots . length ; i ++ ) {
57+ const slot = _dynamicSlots [ i ]
58+ // array of dynamic slot generated by <template v-for="..." #[...]>
59+ if ( isArray ( slot ) ) {
60+ for ( let j = 0 ; j < slot . length ; j ++ ) {
61+ slots [ slot [ j ] . name ] = withCtx ( slot [ j ] . fn )
62+ dynamicSlotKeys [ slot [ j ] . name ] = true
6863 }
64+ } else if ( slot ) {
65+ // conditional single slot generated by <template v-if="..." #foo>
66+ slots [ slot . name ] = withCtx (
67+ slot . key
68+ ? ( ...args : any [ ] ) => {
69+ const res = slot . fn ( ...args )
70+ // attach branch key so each conditional branch is considered a
71+ // different fragment
72+ if ( res ) ( res as any ) . key = slot . key
73+ return res
74+ }
75+ : slot . fn ,
76+ )
77+ dynamicSlotKeys [ slot . name ] = true
6978 }
70- // delete stale slots
71- for ( const key in dynamicSlotKeys ) {
72- if (
73- ! _dynamicSlots . some ( slot =>
74- isArray ( slot )
75- ? slot . some ( s => s . name === key )
76- : slot ?. name === key ,
77- )
78- ) {
79- delete slots [ key ]
80- }
79+ }
80+ // delete stale slots
81+ for ( const key in dynamicSlotKeys ) {
82+ if (
83+ ! _dynamicSlots . some ( slot =>
84+ isArray ( slot ) ? slot . some ( s => s . name === key ) : slot ?. name === key ,
85+ )
86+ ) {
87+ delete slots [ key ]
8188 }
82- } ,
83- undefined ,
84- { scheduler : createVaporPreScheduler ( instance ) } ,
85- )
89+ }
90+ } )
8691 }
8792
8893 instance . slots = slots
@@ -98,3 +103,56 @@ export const initSlots = (
98103 }
99104 }
100105}
106+
107+ export function createSlot (
108+ name : string | ( ( ) => string ) ,
109+ binds ?: Record < string , ( ( ) => unknown ) | undefined > ,
110+ fallback ?: ( ) => Block ,
111+ ) : Block {
112+ let block : Block | undefined
113+ let branch : Slot | undefined
114+ let oldBranch : Slot | undefined
115+ let parent : ParentNode | undefined | null
116+ let scope : EffectScope | undefined
117+ const isDynamicName = isFunction ( name )
118+ const instance = currentInstance !
119+ const { slots } = instance
120+
121+ // When not using dynamic slots, simplify the process to improve performance
122+ if ( ! isDynamicName && ! isReactive ( slots ) ) {
123+ if ( ( branch = slots [ name ] || fallback ) ) {
124+ return branch ( binds )
125+ } else {
126+ return [ ]
127+ }
128+ }
129+
130+ const getName = isDynamicName ? name : ( ) => name
131+ const anchor = __DEV__ ? createComment ( 'slot' ) : createTextNode ( )
132+ const fragment : Fragment = {
133+ nodes : [ ] ,
134+ anchor,
135+ [ fragmentKey ] : true ,
136+ }
137+
138+ // TODO lifecycle hooks
139+ renderEffect ( ( ) => {
140+ if ( ( branch = slots [ getName ( ) ] || fallback ) !== oldBranch ) {
141+ parent ||= anchor . parentNode
142+ if ( block ) {
143+ scope ! . stop ( )
144+ remove ( block , parent ! )
145+ }
146+ if ( ( oldBranch = branch ) ) {
147+ scope = effectScope ( )
148+ fragment . nodes = block = scope . run ( ( ) => branch ! ( binds ) ) !
149+ parent && insert ( block , parent , anchor )
150+ } else {
151+ scope = block = undefined
152+ fragment . nodes = [ ]
153+ }
154+ }
155+ } )
156+
157+ return fragment
158+ }
0 commit comments