Blockless redux (#10794)

* null out teardown function

* add managed effects to effect tree

* let the code breathe a bit

* bit more

* man, that's better

* create correct parent-child relationship between if block and its branches

* remove unnecessary arg

* each blocks already have the correct parent-child relationship so i guess we can get rid of this

* simplify

* unused import

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/10799/head
Rich Harris 2 years ago committed by GitHub
parent 924f0611f7
commit ffb27f667a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -10,7 +10,7 @@ import { current_block, execute_effect } from '../../runtime.js';
import { destroy_effect, render_effect } from '../../reactivity/effects.js'; import { destroy_effect, render_effect } from '../../reactivity/effects.js';
import { trigger_transitions } from '../elements/transitions.js'; import { trigger_transitions } from '../elements/transitions.js';
/** @returns {import('../../types.js').IfBlock} */ /** @returns {import('#client').IfBlock} */
function create_if_block() { function create_if_block() {
return { return {
// alternate transitions // alternate transitions
@ -26,7 +26,7 @@ function create_if_block() {
// effect // effect
e: null, e: null,
// parent // parent
p: /** @type {import('../../types.js').Block} */ (current_block), p: /** @type {import('#client').Block} */ (current_block),
// transition // transition
r: null, r: null,
// type // type
@ -45,137 +45,157 @@ function create_if_block() {
*/ */
export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) { export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn) {
const block = create_if_block(); const block = create_if_block();
hydrate_block_anchor(anchor_node); 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 */ /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
let mismatch = false; let mismatch = false;
/** @type {null | import('../../types.js').TemplateNode | Array<import('../../types.js').TemplateNode>} */ /** @type {null | import('#client').TemplateNode | Array<import('#client').TemplateNode>} */
let consequent_dom = null; let consequent_dom = null;
/** @type {null | import('../../types.js').TemplateNode | Array<import('../../types.js').TemplateNode>} */
/** @type {null | import('#client').TemplateNode | Array<import('#client').TemplateNode>} */
let alternate_dom = null; let alternate_dom = null;
let has_mounted = false; let has_mounted = false;
/** /**
* @type {import('../../types.js').Effect | null} * @type {import('#client').Effect | null}
*/ */
let current_branch_effect = null; let current_branch_effect = null;
const if_effect = render_effect( /** @type {import('#client').Effect} */
() => { let consequent_effect;
const result = !!condition_fn();
if (block.v !== result || !has_mounted) { /** @type {import('#client').Effect} */
block.v = result; let alternate_effect;
if (has_mounted) {
const consequent_transitions = block.c; const if_effect = render_effect(() => {
const alternate_transitions = block.a; const result = !!condition_fn();
if (result) {
if (alternate_transitions === null || alternate_transitions.size === 0) { if (block.v !== result || !has_mounted) {
execute_effect(alternate_effect); block.v = result;
} else {
trigger_transitions(alternate_transitions, 'out'); if (has_mounted) {
} const consequent_transitions = block.c;
if (consequent_transitions === null || consequent_transitions.size === 0) { const alternate_transitions = block.a;
execute_effect(consequent_effect);
} else { if (result) {
trigger_transitions(consequent_transitions, 'in'); if (alternate_transitions === null || alternate_transitions.size === 0) {
} execute_effect(alternate_effect);
} else { } else {
if (consequent_transitions === null || consequent_transitions.size === 0) { trigger_transitions(alternate_transitions, 'out');
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');
}
} }
} else if (hydrating) {
const comment_text = /** @type {Comment} */ (current_hydration_fragment?.[0])?.data; if (consequent_transitions === null || consequent_transitions.size === 0) {
if ( execute_effect(consequent_effect);
!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 { } else {
// Remove the ssr:if comment node or else it will confuse the subsequent hydration algorithm trigger_transitions(consequent_transitions, 'in');
current_hydration_fragment.shift(); }
} 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; } else if (hydrating) {
} const comment_text = /** @type {Comment} */ (current_hydration_fragment?.[0])?.data;
},
block, if (
false !comment_text ||
); (comment_text === 'ssr:if:true' && !result) ||
// Managed effect (comment_text === 'ssr:if:false' && result)
const consequent_effect = render_effect( ) {
( // Hydration mismatch: remove everything inside the anchor and start fresh.
/** @type {any} */ _, // This could happen using when `{#if browser} .. {/if}` in SvelteKit.
/** @type {import('../../types.js').Effect | null} */ consequent_effect remove(current_hydration_fragment);
) => { set_current_hydration_fragment(null);
const result = block.v; mismatch = true;
if (!result && consequent_dom !== null) { } else {
remove(consequent_dom); // Remove the ssr:if comment node or else it will confuse the subsequent hydration algorithm
consequent_dom = null; current_hydration_fragment.shift();
}
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;
} }
block.d = null;
}, has_mounted = true;
block, }
true
); // create these here so they have the correct parent/child relationship
block.ce = consequent_effect; consequent_effect ??= render_effect(
// Managed effect (/** @type {any} */ _, /** @type {import('#client').Effect | null} */ consequent_effect) => {
const alternate_effect = render_effect( const result = block.v;
(
/** @type {any} */ _, if (!result && consequent_dom !== null) {
/** @type {import('../../types.js').Effect | null} */ alternate_effect remove(consequent_dom);
) => { consequent_dom = null;
const result = block.v; }
if (result && alternate_dom !== null) {
remove(alternate_dom); if (result && current_branch_effect !== consequent_effect) {
alternate_dom = null; consequent_fn(anchor_node);
} if (mismatch && current_branch_effect === null) {
if (!result && current_branch_effect !== alternate_effect) { // Set fragment so that Svelte continues to operate in hydration mode
if (alternate_fn !== null) { set_current_hydration_fragment([]);
alternate_fn(anchor_node); }
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 block.d = null;
set_current_hydration_fragment([]); },
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; if (!result && current_branch_effect !== alternate_effect) {
} if (alternate_fn !== null) {
block.d = null; alternate_fn(anchor_node);
}, }
block,
true if (mismatch && current_branch_effect === null) {
); // Set fragment so that Svelte continues to operate in hydration mode
block.ae = alternate_effect; 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_effect.ondestroy = () => {
if (consequent_dom !== null) { if (consequent_dom !== null) {
remove(consequent_dom); remove(consequent_dom);
} }
if (alternate_dom !== null) { if (alternate_dom !== null) {
remove(alternate_dom); remove(alternate_dom);
} }
destroy_effect(consequent_effect); destroy_effect(consequent_effect);
destroy_effect(alternate_effect); destroy_effect(alternate_effect);
}; };
block.e = if_effect; block.e = if_effect;
} }

@ -5,7 +5,6 @@ import {
current_effect, current_effect,
current_reaction, current_reaction,
destroy_children, destroy_children,
flush_local_render_effects,
get, get,
remove_reactions, remove_reactions,
schedule_effect, schedule_effect,
@ -42,13 +41,11 @@ function create_effect(type, fn, sync, block = current_block, init = true) {
signal.l = current_effect.l + 1; signal.l = current_effect.l + 1;
} }
if ((type & MANAGED) === 0) { if (current_reaction !== null) {
if (current_reaction !== null) { if (current_reaction.effects === null) {
if (current_reaction.effects === null) { current_reaction.effects = [signal];
current_reaction.effects = [signal]; } else {
} else { current_reaction.effects.push(signal);
current_reaction.effects.push(signal);
}
} }
} }
@ -230,5 +227,12 @@ export function destroy_effect(signal) {
signal.teardown?.(); signal.teardown?.();
signal.ondestroy?.(); 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;
} }

@ -10,8 +10,6 @@ import {
import { unstate } from './proxy.js'; import { unstate } from './proxy.js';
import { destroy_effect, pre_effect } from './reactivity/effects.js'; import { destroy_effect, pre_effect } from './reactivity/effects.js';
import { import {
EACH_BLOCK,
IF_BLOCK,
EFFECT, EFFECT,
PRE_EFFECT, PRE_EFFECT,
RENDER_EFFECT, RENDER_EFFECT,
@ -359,7 +357,10 @@ export function remove_reactions(signal, start_index) {
export function destroy_children(signal) { export function destroy_children(signal) {
if (signal.effects) { if (signal.effects) {
for (var i = 0; i < signal.effects.length; i += 1) { 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; signal.effects = null;
} }
@ -750,15 +751,14 @@ export function invalidate_inner_signals(fn) {
/** /**
* @param {import('#client').Effect} signal * @param {import('#client').Effect} signal
* @param {boolean} inert * @param {boolean} inert
* @param {Set<import('#client').Block>} [visited_blocks]
* @returns {void} * @returns {void}
*/ */
function mark_subtree_children_inert(signal, inert, visited_blocks) { function mark_subtree_children_inert(signal, inert) {
const effects = signal.effects; const effects = signal.effects;
if (effects !== null) { if (effects !== null) {
for (var i = 0; i < effects.length; i++) { for (var i = 0; i < effects.length; i++) {
const effect = effects[i]; mark_subtree_inert(effects[i], inert);
mark_subtree_inert(effect, inert, visited_blocks);
} }
} }
} }
@ -766,46 +766,20 @@ function mark_subtree_children_inert(signal, inert, visited_blocks) {
/** /**
* @param {import('#client').Effect} signal * @param {import('#client').Effect} signal
* @param {boolean} inert * @param {boolean} inert
* @param {Set<import('#client').Block>} [visited_blocks]
* @returns {void} * @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 flags = signal.f;
const is_already_inert = (flags & INERT) !== 0; const is_already_inert = (flags & INERT) !== 0;
if (is_already_inert !== inert) { if (is_already_inert !== inert) {
signal.f ^= INERT; signal.f ^= INERT;
if (!inert && (flags & CLEAN) === 0) { if (!inert && (flags & CLEAN) === 0) {
schedule_effect(signal, false); 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);
} }
/** /**

Loading…
Cancel
Save