@@ -15,9 +15,11 @@ import { untrack } from '../../runtime.js';
15
15
import {
16
16
block ,
17
17
branch ,
18
+ destroy_effect ,
18
19
effect ,
20
+ out ,
21
+ pause_children ,
19
22
pause_effect ,
20
- pause_effects ,
21
23
resume_effect
22
24
} from '../../reactivity/effects.js' ;
23
25
import { source , mutable_source , set } from '../../reactivity/sources.js' ;
@@ -39,6 +41,38 @@ export function set_current_each_item(item) {
39
41
current_each_item = item ;
40
42
}
41
43
44
+ /**
45
+ * Pause multiple effects simultaneously, and coordinate their
46
+ * subsequent destruction. Used in each blocks
47
+ * @param {import('#client').Effect[] } effects
48
+ * @param {null | Node } controlled_anchor
49
+ * @param {() => void } [callback]
50
+ */
51
+ function pause_effects ( effects , controlled_anchor , callback ) {
52
+ /** @type {import('#client').TransitionManager[] } */
53
+ var transitions = [ ] ;
54
+ var length = effects . length ;
55
+
56
+ for ( var i = 0 ; i < length ; i ++ ) {
57
+ pause_children ( effects [ i ] , transitions , true ) ;
58
+ }
59
+
60
+ // If we have a controlled anchor, it means that the each block is inside a single
61
+ // DOM element, so we can apply a fast-path for clearing the contents of the element.
62
+ if ( effects . length > 0 && transitions . length === 0 && controlled_anchor !== null ) {
63
+ const each_element = /** @type {Element } */ ( controlled_anchor . parentNode ) ;
64
+ each_element . textContent = '' ;
65
+ each_element . append ( controlled_anchor ) ;
66
+ }
67
+
68
+ out ( transitions , ( ) => {
69
+ for ( var i = 0 ; i < length ; i ++ ) {
70
+ destroy_effect ( effects [ i ] ) ;
71
+ }
72
+ if ( callback !== undefined ) callback ( ) ;
73
+ } ) ;
74
+ }
75
+
42
76
/**
43
77
* @template V
44
78
* @param {Element | Comment } anchor The next sibling node, or the parent node if this is a 'controlled' block
@@ -145,7 +179,6 @@ function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn, re
145
179
}
146
180
147
181
if ( ! hydrating ) {
148
- // TODO add 'empty controlled block' optimisation here
149
182
reconcile_fn ( array , state , anchor , render_fn , flags , keys ) ;
150
183
}
151
184
@@ -244,7 +277,9 @@ function reconcile_indexed_array(array, state, anchor, render_fn, flags) {
244
277
effects . push ( a_items [ i ] . e ) ;
245
278
}
246
279
247
- pause_effects ( effects , ( ) => {
280
+ var controlled_anchor = ( flags & EACH_IS_CONTROLLED ) !== 0 && b === 0 ? anchor : null ;
281
+
282
+ pause_effects ( effects , controlled_anchor , ( ) => {
248
283
state . items . length = b ;
249
284
} ) ;
250
285
}
@@ -274,6 +309,7 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
274
309
275
310
var is_animated = ( flags & EACH_IS_ANIMATED ) !== 0 ;
276
311
var should_update = ( flags & ( EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE ) ) !== 0 ;
312
+ var is_controlled = ( flags & EACH_IS_CONTROLLED ) !== 0 ;
277
313
var start = 0 ;
278
314
var item ;
279
315
@@ -381,6 +417,12 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
381
417
// I fully understand this part)
382
418
if ( moved ) {
383
419
mark_lis ( sources ) ;
420
+ } else if ( is_controlled && to_destroy . length === a_items . length ) {
421
+ // We can optimize the case where we change all items of the each block to an entirely new set of items.
422
+ // In this case we can first clear the DOM fast, along with all their effect, then we can continue
423
+ // with the normal logic that appends them.
424
+ pause_effects ( to_destroy , anchor ) ;
425
+ to_destroy = [ ] ;
384
426
}
385
427
386
428
// working from the back, insert new or moved items
@@ -421,9 +463,9 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
421
463
} ) ;
422
464
}
423
465
424
- // TODO: would be good to avoid this closure in the case where we have no
425
- // transitions at all. It would make it far more JIT friendly in the hot cases.
426
- pause_effects ( to_destroy , ( ) => {
466
+ var controlled_anchor = is_controlled && b === 0 ? anchor : null ;
467
+
468
+ pause_effects ( to_destroy , controlled_anchor , ( ) => {
427
469
state . items = b_items ;
428
470
} ) ;
429
471
}
0 commit comments