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'
6
16
7
17
// TODO: SSR
8
18
@@ -29,7 +39,7 @@ export const initSlots = (
29
39
rawSlots : InternalSlots | null = null ,
30
40
dynamicSlots : DynamicSlots | null = null ,
31
41
) => {
32
- const slots : InternalSlots = { }
42
+ let slots : InternalSlots = { }
33
43
34
44
for ( const key in rawSlots ) {
35
45
const slot = rawSlots [ key ]
@@ -39,50 +49,45 @@ export const initSlots = (
39
49
}
40
50
41
51
if ( dynamicSlots ) {
52
+ slots = shallowReactive ( slots )
42
53
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
68
63
}
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
69
78
}
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 ]
81
88
}
82
- } ,
83
- undefined ,
84
- { scheduler : createVaporPreScheduler ( instance ) } ,
85
- )
89
+ }
90
+ } )
86
91
}
87
92
88
93
instance . slots = slots
@@ -98,3 +103,56 @@ export const initSlots = (
98
103
}
99
104
}
100
105
}
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