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 { 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,34 +45,48 @@ 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<import('../../types.js').TemplateNode>} */
/** @type {null | import('#client').TemplateNode | Array<import('#client').TemplateNode>} */
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 has_mounted = false;
/**
* @type {import('../../types.js').Effect | null}
* @type {import('#client').Effect | null}
*/
let current_branch_effect = null;
const if_effect = render_effect(
() => {
/** @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 {
trigger_transitions(alternate_transitions, 'out');
}
if (consequent_transitions === null || consequent_transitions.size === 0) {
execute_effect(consequent_effect);
} else {
@ -84,6 +98,7 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
} else {
trigger_transitions(consequent_transitions, 'out');
}
if (alternate_transitions === null || alternate_transitions.size === 0) {
execute_effect(alternate_effect);
} else {
@ -92,6 +107,7 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
}
} else if (hydrating) {
const comment_text = /** @type {Comment} */ (current_hydration_fragment?.[0])?.data;
if (
!comment_text ||
(comment_text === 'ssr:if:true' && !result) ||
@ -107,23 +123,20 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
current_hydration_fragment.shift();
}
}
has_mounted = true;
}
},
block,
false
);
// Managed effect
const consequent_effect = render_effect(
(
/** @type {any} */ _,
/** @type {import('../../types.js').Effect | null} */ consequent_effect
) => {
// 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) {
@ -133,31 +146,33 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
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
) => {
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;
}
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;
}
@ -167,15 +182,20 @@ export function if_block(anchor_node, condition_fn, consequent_fn, alternate_fn)
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;
}

@ -5,7 +5,6 @@ import {
current_effect,
current_reaction,
destroy_children,
flush_local_render_effects,
get,
remove_reactions,
schedule_effect,
@ -42,7 +41,6 @@ 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];
@ -50,7 +48,6 @@ function create_effect(type, fn, sync, block = current_block, init = true) {
current_reaction.effects.push(signal);
}
}
}
if (init) {
schedule_effect(signal, sync);
@ -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;
}

@ -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<import('#client').Block>} [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<import('#client').Block>} [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);
}
/**

Loading…
Cancel
Save