Skip to content

Commit 455fa89

Browse files
authored
Code golf each transitions (#9476)
* chore: improve code size of transitions * Add changeset
1 parent e3e1fef commit 455fa89

File tree

3 files changed

+123
-125
lines changed

3 files changed

+123
-125
lines changed

.changeset/cuddly-pianos-drop.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
chore: improve bundle code size

packages/svelte/src/internal/client/render.js

Lines changed: 10 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,10 @@ import {
5353
flushSync,
5454
expose,
5555
safe_not_equal,
56-
managed_pre_effect,
5756
current_block,
5857
set_signal_value,
5958
source,
6059
managed_effect,
61-
mark_subtree_inert,
6260
safe_equal,
6361
push,
6462
current_component_context,
@@ -73,7 +71,7 @@ import {
7371
} from './hydration.js';
7472
import { array_from, define_property, get_descriptor, get_descriptors, is_array } from './utils.js';
7573
import { is_promise } from '../common.js';
76-
import { bind_transition } from './transitions.js';
74+
import { bind_transition, remove_in_transitions, trigger_transitions } from './transitions.js';
7775

7876
/** @type {Set<string>} */
7977
const all_registerd_events = new Set();
@@ -82,7 +80,7 @@ const all_registerd_events = new Set();
8280
const root_event_handles = new Set();
8381

8482
/** @returns {Text} */
85-
function empty() {
83+
export function empty() {
8684
return document.createTextNode('');
8785
}
8886

@@ -1370,11 +1368,7 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
13701368
consequent_transitions.add(transition);
13711369
transition.finished(() => {
13721370
consequent_transitions.delete(transition);
1373-
for (let other of consequent_transitions) {
1374-
if (other.direction === 'in') {
1375-
consequent_transitions.delete(other);
1376-
}
1377-
}
1371+
remove_in_transitions(consequent_transitions);
13781372
if (consequent_transitions.size === 0) {
13791373
execute_effect(consequent_effect);
13801374
}
@@ -1383,11 +1377,7 @@ function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
13831377
alternate_transitions.add(transition);
13841378
transition.finished(() => {
13851379
alternate_transitions.delete(transition);
1386-
for (let other of alternate_transitions) {
1387-
if (other.direction === 'in') {
1388-
alternate_transitions.delete(other);
1389-
}
1390-
}
1380+
remove_in_transitions(alternate_transitions);
13911381
if (alternate_transitions.size === 0) {
13921382
execute_effect(alternate_effect);
13931383
}
@@ -1675,11 +1665,7 @@ export function component(anchor_node, component_fn, render_fn) {
16751665
transitions.add(transition);
16761666
transition.finished(() => {
16771667
transitions.delete(transition);
1678-
for (let other of transitions) {
1679-
if (other.direction === 'in') {
1680-
transitions.delete(other);
1681-
}
1682-
}
1668+
remove_in_transitions(transitions);
16831669
if (transitions.size === 0) {
16841670
if (render.effect !== null) {
16851671
if (render.dom !== null) {
@@ -1806,11 +1792,7 @@ function await_block(anchor_node, input, pending_fn, then_fn, catch_fn) {
18061792
transitions.add(transition);
18071793
transition.finished(() => {
18081794
transitions.delete(transition);
1809-
for (let other of transitions) {
1810-
if (other.direction === 'in') {
1811-
transitions.delete(other);
1812-
}
1813-
}
1795+
remove_in_transitions(transitions);
18141796
if (transitions.size === 0) {
18151797
if (render.effect !== null) {
18161798
if (render.dom !== null) {
@@ -1864,6 +1846,7 @@ function await_block(anchor_node, input, pending_fn, then_fn, catch_fn) {
18641846
return;
18651847
}
18661848
const transitions = render.transitions;
1849+
remove_in_transitions(transitions);
18671850
if (transitions.size === 0) {
18681851
if (render.dom !== null) {
18691852
remove(render.dom);
@@ -1968,11 +1951,7 @@ export function key(anchor_node, key, render_fn) {
19681951
transitions.add(transition);
19691952
transition.finished(() => {
19701953
transitions.delete(transition);
1971-
for (let other of transitions) {
1972-
if (other.direction === 'in') {
1973-
transitions.delete(other);
1974-
}
1975-
}
1954+
remove_in_transitions(transitions);
19761955
if (transitions.size === 0) {
19771956
if (render.effect !== null) {
19781957
if (render.dom !== null) {
@@ -2013,6 +1992,7 @@ export function key(anchor_node, key, render_fn) {
20131992
return;
20141993
}
20151994
const transitions = render.transitions;
1995+
remove_in_transitions(transitions);
20161996
if (transitions.size === 0) {
20171997
if (render.dom !== null) {
20181998
remove(render.dom);
@@ -2140,96 +2120,6 @@ export function destroy_each_item_block(
21402120
}
21412121
}
21422122

2143-
/**
2144-
* @this {import('./types.js').EachItemBlock}
2145-
* @param {import('./types.js').Transition} transition
2146-
* @returns {void}
2147-
*/
2148-
function each_item_transition(transition) {
2149-
const block = this;
2150-
const each_block = block.parent;
2151-
const is_controlled = (each_block.flags & EACH_IS_CONTROLLED) !== 0;
2152-
// Disable optimization
2153-
if (is_controlled) {
2154-
const anchor = empty();
2155-
each_block.flags ^= EACH_IS_CONTROLLED;
2156-
append_child(/** @type {Element} */ (each_block.anchor), anchor);
2157-
each_block.anchor = anchor;
2158-
}
2159-
if (transition.direction === 'key' && (each_block.flags & EACH_IS_ANIMATED) === 0) {
2160-
each_block.flags |= EACH_IS_ANIMATED;
2161-
}
2162-
let transitions = block.transitions;
2163-
if (transitions === null) {
2164-
block.transitions = transitions = new Set();
2165-
}
2166-
transition.finished(() => {
2167-
if (transitions !== null) {
2168-
transitions.delete(transition);
2169-
if (transition.direction !== 'key') {
2170-
for (let other of transitions) {
2171-
if (other.direction === 'key' || other.direction === 'in') {
2172-
transitions.delete(other);
2173-
}
2174-
}
2175-
if (transitions.size === 0) {
2176-
block.transitions = null;
2177-
destroy_each_item_block(block, null, true);
2178-
}
2179-
}
2180-
}
2181-
});
2182-
transitions.add(transition);
2183-
}
2184-
2185-
/**
2186-
* @param {Set<import('./types.js').Transition>} transitions
2187-
* @param {'in' | 'out' | 'key'} target_direction
2188-
* @param {DOMRect} [from]
2189-
* @returns {void}
2190-
*/
2191-
export function trigger_transitions(transitions, target_direction, from) {
2192-
/** @type {Array<() => void>} */
2193-
const outros = [];
2194-
for (const transition of transitions) {
2195-
const direction = transition.direction;
2196-
if (target_direction === 'in') {
2197-
if (direction === 'in' || direction === 'both') {
2198-
if (direction === 'in') {
2199-
transition.cancel();
2200-
}
2201-
transition.in();
2202-
} else {
2203-
transition.cancel();
2204-
}
2205-
transition.dom.inert = false;
2206-
mark_subtree_inert(transition.effect, false);
2207-
} else if (target_direction === 'key') {
2208-
if (direction === 'key') {
2209-
transition.payload = transition.init(/** @type {DOMRect} */ (from));
2210-
transition.in();
2211-
}
2212-
} else {
2213-
if (direction === 'out' || direction === 'both') {
2214-
transition.payload = transition.init();
2215-
outros.push(transition.out);
2216-
}
2217-
transition.dom.inert = true;
2218-
mark_subtree_inert(transition.effect, true);
2219-
}
2220-
}
2221-
if (outros.length > 0) {
2222-
// Defer the outros to a microtask
2223-
const e = managed_pre_effect(() => {
2224-
destroy_signal(e);
2225-
const e2 = managed_effect(() => {
2226-
destroy_signal(e2);
2227-
outros.forEach(/** @param {any} o */ (o) => o());
2228-
});
2229-
}, false);
2230-
}
2231-
}
2232-
22332123
/**
22342124
* @template V
22352125
* @param {V} item
@@ -2243,7 +2133,6 @@ export function each_item_block(item, key, index, render_fn, flags) {
22432133
const item_value = (flags & EACH_ITEM_REACTIVE) === 0 ? item : source(item);
22442134
const index_value = (flags & EACH_INDEX_REACTIVE) === 0 ? index : source(index);
22452135
const block = create_each_item_block(item_value, index_value, key);
2246-
block.transition = each_item_transition;
22472136
const effect = render_effect(
22482137
/** @param {import('./types.js').EachItemBlock} block */
22492138
(block) => {
@@ -2291,11 +2180,7 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
22912180
transitions.add(transition);
22922181
transition.finished(() => {
22932182
transitions.delete(transition);
2294-
for (let other of transitions) {
2295-
if (other.direction === 'in') {
2296-
transitions.delete(other);
2297-
}
2298-
}
2183+
remove_in_transitions(transitions);
22992184
if (transitions.size === 0) {
23002185
if (fallback.effect !== null) {
23012186
if (fallback.dom !== null) {

packages/svelte/src/internal/client/transitions.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { EACH_IS_ANIMATED, EACH_IS_CONTROLLED } from '../../constants.js';
12
import {
23
AWAIT_BLOCK,
34
DYNAMIC_COMPONENT_BLOCK,
@@ -7,12 +8,16 @@ import {
78
KEY_BLOCK,
89
ROOT_BLOCK
910
} from './block.js';
11+
import { append_child } from './operations.js';
12+
import { destroy_each_item_block, empty } from './render.js';
1013
import {
1114
current_block,
1215
current_effect,
1316
destroy_signal,
1417
effect,
18+
managed_effect,
1519
managed_pre_effect,
20+
mark_subtree_inert,
1621
untrack
1722
} from './runtime.js';
1823
import { raf } from './timing.js';
@@ -414,6 +419,8 @@ export function bind_transition(dom, transition_fn, props_fn, direction, global)
414419
while (transition_block !== null) {
415420
if (is_transition_block(transition_block)) {
416421
if (transition_block.type === EACH_ITEM_BLOCK) {
422+
// Lazily apply the each block transition
423+
transition_block.transition = each_item_transition;
417424
transition_block = transition_block.parent;
418425
} else if (transition_block.type === AWAIT_BLOCK && transition_block.pending) {
419426
skip_intro = false;
@@ -496,3 +503,104 @@ export function bind_transition(dom, transition_fn, props_fn, direction, global)
496503
});
497504
}
498505
}
506+
507+
/**
508+
* @param {Set<import('./types.js').Transition>} transitions
509+
*/
510+
export function remove_in_transitions(transitions) {
511+
for (let other of transitions) {
512+
if (other.direction === 'in') {
513+
transitions.delete(other);
514+
}
515+
}
516+
}
517+
518+
/**
519+
* @param {Set<import('./types.js').Transition>} transitions
520+
* @param {'in' | 'out' | 'key'} target_direction
521+
* @param {DOMRect} [from]
522+
* @returns {void}
523+
*/
524+
export function trigger_transitions(transitions, target_direction, from) {
525+
/** @type {Array<() => void>} */
526+
const outros = [];
527+
for (const transition of transitions) {
528+
const direction = transition.direction;
529+
if (target_direction === 'in') {
530+
if (direction === 'in' || direction === 'both') {
531+
if (direction === 'in') {
532+
transition.cancel();
533+
}
534+
transition.in();
535+
} else {
536+
transition.cancel();
537+
}
538+
transition.dom.inert = false;
539+
mark_subtree_inert(transition.effect, false);
540+
} else if (target_direction === 'key') {
541+
if (direction === 'key') {
542+
transition.payload = transition.init(/** @type {DOMRect} */ (from));
543+
transition.in();
544+
}
545+
} else {
546+
if (direction === 'out' || direction === 'both') {
547+
transition.payload = transition.init();
548+
outros.push(transition.out);
549+
}
550+
transition.dom.inert = true;
551+
mark_subtree_inert(transition.effect, true);
552+
}
553+
}
554+
if (outros.length > 0) {
555+
// Defer the outros to a microtask
556+
const e = managed_pre_effect(() => {
557+
destroy_signal(e);
558+
const e2 = managed_effect(() => {
559+
destroy_signal(e2);
560+
outros.forEach(/** @param {any} o */ (o) => o());
561+
});
562+
}, false);
563+
}
564+
}
565+
566+
/**
567+
* @this {import('./types.js').EachItemBlock}
568+
* @param {import('./types.js').Transition} transition
569+
* @returns {void}
570+
*/
571+
function each_item_transition(transition) {
572+
const block = this;
573+
const each_block = block.parent;
574+
const is_controlled = (each_block.flags & EACH_IS_CONTROLLED) !== 0;
575+
// Disable optimization
576+
if (is_controlled) {
577+
const anchor = empty();
578+
each_block.flags ^= EACH_IS_CONTROLLED;
579+
append_child(/** @type {Element} */ (each_block.anchor), anchor);
580+
each_block.anchor = anchor;
581+
}
582+
if (transition.direction === 'key' && (each_block.flags & EACH_IS_ANIMATED) === 0) {
583+
each_block.flags |= EACH_IS_ANIMATED;
584+
}
585+
let transitions = block.transitions;
586+
if (transitions === null) {
587+
block.transitions = transitions = new Set();
588+
}
589+
transition.finished(() => {
590+
if (transitions !== null) {
591+
transitions.delete(transition);
592+
if (transition.direction !== 'key') {
593+
for (let other of transitions) {
594+
if (other.direction === 'key' || other.direction === 'in') {
595+
transitions.delete(other);
596+
}
597+
}
598+
if (transitions.size === 0) {
599+
block.transitions = null;
600+
destroy_each_item_block(block, null, true);
601+
}
602+
}
603+
}
604+
});
605+
transitions.add(transition);
606+
}

0 commit comments

Comments
 (0)