diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js index 9f8f717508..a8292a9f02 100644 --- a/packages/svelte/src/internal/client/dom/blocks/if.js +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -10,7 +10,7 @@ import { current_block, execute_effect } from '../../runtime.js'; import { destroy_effect, render_effect } from '../../reactivity/effects.js'; import { trigger_transitions } from '../elements/transitions.js'; -/** @returns {import('../../types.js').IfBlock} */ +/** @returns {import('#client').IfBlock} */ function create_if_block() { return { // alternate transitions @@ -26,7 +26,7 @@ function create_if_block() { // effect e: null, // parent - p: /** @type {import('../../types.js').Block} */ (current_block), + p: /** @type {import('#client').Block} */ (current_block), // transition r: null, // type @@ -45,137 +45,157 @@ function create_if_block() { */ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) { const block = create_if_block(); + hydrate_block_anchor(anchor_node); + /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */ let mismatch = false; - /** @type {null | import('../../types.js').TemplateNode | Array} */ + /** @type {null | import('#client').TemplateNode | Array} */ let consequent_dom = null; - /** @type {null | import('../../types.js').TemplateNode | Array} */ + + /** @type {null | import('#client').TemplateNode | Array} */ let alternate_dom = null; + let has_mounted = false; + /** - * @type {import('../../types.js').Effect | null} + * @type {import('#client').Effect | null} */ let current_branch_effect = null; - const if_effect = render_effect( - () => { - const result = !!condition_fn(); - if (block.v !== result || !has_mounted) { - block.v = result; - if (has_mounted) { - const consequent_transitions = block.c; - const alternate_transitions = block.a; - if (result) { - if (alternate_transitions === null || alternate_transitions.size === 0) { - execute_effect(alternate_effect); - } else { - trigger_transitions(alternate_transitions, 'out'); - } - if (consequent_transitions === null || consequent_transitions.size === 0) { - execute_effect(consequent_effect); - } else { - trigger_transitions(consequent_transitions, 'in'); - } + /** @type {import('#client').Effect} */ + let consequent_effect; + + /** @type {import('#client').Effect} */ + let alternate_effect; + + const if_effect = render_effect(() => { + const result = !!condition_fn(); + + if (block.v !== result || !has_mounted) { + block.v = result; + + if (has_mounted) { + const consequent_transitions = block.c; + const alternate_transitions = block.a; + + if (result) { + if (alternate_transitions === null || alternate_transitions.size === 0) { + execute_effect(alternate_effect); } else { - if (consequent_transitions === null || consequent_transitions.size === 0) { - execute_effect(consequent_effect); - } else { - trigger_transitions(consequent_transitions, 'out'); - } - if (alternate_transitions === null || alternate_transitions.size === 0) { - execute_effect(alternate_effect); - } else { - trigger_transitions(alternate_transitions, 'in'); - } + trigger_transitions(alternate_transitions, 'out'); } - } else if (hydrating) { - const comment_text = /** @type {Comment} */ (current_hydration_fragment?.[0])?.data; - if ( - !comment_text || - (comment_text === 'ssr:if:true' && !result) || - (comment_text === 'ssr:if:false' && result) - ) { - // Hydration mismatch: remove everything inside the anchor and start fresh. - // This could happen using when `{#if browser} .. {/if}` in SvelteKit. - remove(current_hydration_fragment); - set_current_hydration_fragment(null); - mismatch = true; + + if (consequent_transitions === null || consequent_transitions.size === 0) { + execute_effect(consequent_effect); } else { - // Remove the ssr:if comment node or else it will confuse the subsequent hydration algorithm - current_hydration_fragment.shift(); + trigger_transitions(consequent_transitions, 'in'); + } + } else { + if (consequent_transitions === null || consequent_transitions.size === 0) { + execute_effect(consequent_effect); + } else { + trigger_transitions(consequent_transitions, 'out'); + } + + if (alternate_transitions === null || alternate_transitions.size === 0) { + execute_effect(alternate_effect); + } else { + trigger_transitions(alternate_transitions, 'in'); } } - has_mounted = true; - } - }, - block, - false - ); - // Managed effect - const consequent_effect = render_effect( - ( - /** @type {any} */ _, - /** @type {import('../../types.js').Effect | null} */ consequent_effect - ) => { - const result = block.v; - if (!result && consequent_dom !== null) { - remove(consequent_dom); - consequent_dom = null; - } - if (result && current_branch_effect !== consequent_effect) { - consequent_fn(anchor_node); - if (mismatch && current_branch_effect === null) { - // Set fragment so that Svelte continues to operate in hydration mode - set_current_hydration_fragment([]); + } else if (hydrating) { + const comment_text = /** @type {Comment} */ (current_hydration_fragment?.[0])?.data; + + if ( + !comment_text || + (comment_text === 'ssr:if:true' && !result) || + (comment_text === 'ssr:if:false' && result) + ) { + // Hydration mismatch: remove everything inside the anchor and start fresh. + // This could happen using when `{#if browser} .. {/if}` in SvelteKit. + remove(current_hydration_fragment); + set_current_hydration_fragment(null); + mismatch = true; + } else { + // Remove the ssr:if comment node or else it will confuse the subsequent hydration algorithm + current_hydration_fragment.shift(); } - current_branch_effect = consequent_effect; - consequent_dom = block.d; } - block.d = null; - }, - block, - true - ); - block.ce = consequent_effect; - // Managed effect - const alternate_effect = render_effect( - ( - /** @type {any} */ _, - /** @type {import('../../types.js').Effect | null} */ alternate_effect - ) => { - const result = block.v; - if (result && alternate_dom !== null) { - remove(alternate_dom); - alternate_dom = null; - } - if (!result && current_branch_effect !== alternate_effect) { - if (alternate_fn !== null) { - alternate_fn(anchor_node); + + has_mounted = true; + } + + // create these here so they have the correct parent/child relationship + consequent_effect ??= render_effect( + (/** @type {any} */ _, /** @type {import('#client').Effect | null} */ consequent_effect) => { + const result = block.v; + + if (!result && consequent_dom !== null) { + remove(consequent_dom); + consequent_dom = null; + } + + if (result && current_branch_effect !== consequent_effect) { + consequent_fn(anchor_node); + if (mismatch && current_branch_effect === null) { + // Set fragment so that Svelte continues to operate in hydration mode + set_current_hydration_fragment([]); + } + current_branch_effect = consequent_effect; + consequent_dom = block.d; } - if (mismatch && current_branch_effect === null) { - // Set fragment so that Svelte continues to operate in hydration mode - set_current_hydration_fragment([]); + + block.d = null; + }, + block, + true + ); + block.ce = consequent_effect; + + alternate_effect ??= render_effect( + (/** @type {any} */ _, /** @type {import('#client').Effect | null} */ alternate_effect) => { + const result = block.v; + + if (result && alternate_dom !== null) { + remove(alternate_dom); + alternate_dom = null; } - current_branch_effect = alternate_effect; - alternate_dom = block.d; - } - block.d = null; - }, - block, - true - ); - block.ae = alternate_effect; + + if (!result && current_branch_effect !== alternate_effect) { + if (alternate_fn !== null) { + alternate_fn(anchor_node); + } + + if (mismatch && current_branch_effect === null) { + // Set fragment so that Svelte continues to operate in hydration mode + set_current_hydration_fragment([]); + } + + current_branch_effect = alternate_effect; + alternate_dom = block.d; + } + block.d = null; + }, + block, + true + ); + block.ae = alternate_effect; + }, block); + if_effect.ondestroy = () => { if (consequent_dom !== null) { remove(consequent_dom); } + if (alternate_dom !== null) { remove(alternate_dom); } + destroy_effect(consequent_effect); destroy_effect(alternate_effect); }; + block.e = if_effect; } diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 99c98013d4..4aef848b32 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -5,7 +5,6 @@ import { current_effect, current_reaction, destroy_children, - flush_local_render_effects, get, remove_reactions, schedule_effect, @@ -42,13 +41,11 @@ function create_effect(type, fn, sync, block = current_block, init = true) { signal.l = current_effect.l + 1; } - if ((type & MANAGED) === 0) { - if (current_reaction !== null) { - if (current_reaction.effects === null) { - current_reaction.effects = [signal]; - } else { - current_reaction.effects.push(signal); - } + if (current_reaction !== null) { + if (current_reaction.effects === null) { + current_reaction.effects = [signal]; + } else { + current_reaction.effects.push(signal); } } @@ -230,5 +227,12 @@ export function destroy_effect(signal) { signal.teardown?.(); signal.ondestroy?.(); - signal.fn = signal.effects = signal.ondestroy = signal.ctx = signal.block = signal.deps = null; + signal.fn = + signal.effects = + signal.teardown = + signal.ondestroy = + signal.ctx = + signal.block = + signal.deps = + null; } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 2620cddddd..745342bf6e 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -10,8 +10,6 @@ import { import { unstate } from './proxy.js'; import { destroy_effect, pre_effect } from './reactivity/effects.js'; import { - EACH_BLOCK, - IF_BLOCK, EFFECT, PRE_EFFECT, RENDER_EFFECT, @@ -359,7 +357,10 @@ export function remove_reactions(signal, start_index) { export function destroy_children(signal) { if (signal.effects) { for (var i = 0; i < signal.effects.length; i += 1) { - destroy_effect(signal.effects[i]); + var effect = signal.effects[i]; + if ((effect.f & MANAGED) === 0) { + destroy_effect(effect); + } } signal.effects = null; } @@ -750,15 +751,14 @@ export function invalidate_inner_signals(fn) { /** * @param {import('#client').Effect} signal * @param {boolean} inert - * @param {Set} [visited_blocks] * @returns {void} */ -function mark_subtree_children_inert(signal, inert, visited_blocks) { +function mark_subtree_children_inert(signal, inert) { const effects = signal.effects; + if (effects !== null) { for (var i = 0; i < effects.length; i++) { - const effect = effects[i]; - mark_subtree_inert(effect, inert, visited_blocks); + mark_subtree_inert(effects[i], inert); } } } @@ -766,46 +766,20 @@ function mark_subtree_children_inert(signal, inert, visited_blocks) { /** * @param {import('#client').Effect} signal * @param {boolean} inert - * @param {Set} [visited_blocks] * @returns {void} */ -export function mark_subtree_inert(signal, inert, visited_blocks = new Set()) { +export function mark_subtree_inert(signal, inert) { const flags = signal.f; const is_already_inert = (flags & INERT) !== 0; + if (is_already_inert !== inert) { signal.f ^= INERT; if (!inert && (flags & CLEAN) === 0) { schedule_effect(signal, false); } - // Nested if block effects - const block = signal.block; - if (block !== null && !visited_blocks.has(block)) { - visited_blocks.add(block); - const type = block.t; - if (type === IF_BLOCK) { - const condition_effect = block.e; - if (condition_effect !== null && block !== current_block) { - mark_subtree_inert(condition_effect, inert, visited_blocks); - } - const consequent_effect = block.ce; - if (consequent_effect !== null && block.v) { - mark_subtree_inert(consequent_effect, inert, visited_blocks); - } - const alternate_effect = block.ae; - if (alternate_effect !== null && !block.v) { - mark_subtree_inert(alternate_effect, inert, visited_blocks); - } - } else if (type === EACH_BLOCK) { - const items = block.v; - for (let { e: each_item_effect } of items) { - if (each_item_effect !== null) { - mark_subtree_inert(each_item_effect, inert, visited_blocks); - } - } - } - } } - mark_subtree_children_inert(signal, inert, visited_blocks); + + mark_subtree_children_inert(signal, inert); } /**