fix: only destroy snippets when they have changed (#11267)

* fix: only destroy snippets when they have changed

* tidy up

* changeset

* oops

* tidy up
pull/11260/head
Rich Harris 1 year ago committed by GitHub
parent 5ef1fdf7cc
commit 47ba488cf2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: only destroy snippets when they have changed

@ -20,15 +20,17 @@ export function if_block(
elseif = false elseif = false
) { ) {
/** @type {import('#client').Effect | null} */ /** @type {import('#client').Effect | null} */
let consequent_effect = null; var consequent_effect = null;
/** @type {import('#client').Effect | null} */ /** @type {import('#client').Effect | null} */
let alternate_effect = null; var alternate_effect = null;
/** @type {boolean | null} */ /** @type {boolean | null} */
let condition = null; var condition = null;
const effect = block(() => { var flags = elseif ? EFFECT_TRANSPARENT : 0;
block(() => {
if (condition === (condition = !!get_condition())) return; if (condition === (condition = !!get_condition())) return;
/** 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 */
@ -76,9 +78,5 @@ export function if_block(
// continue in hydration mode // continue in hydration mode
set_hydrating(true); set_hydrating(true);
} }
}); }, flags);
if (elseif) {
effect.f |= EFFECT_TRANSPARENT;
}
} }

@ -1,5 +1,5 @@
import { EFFECT_TRANSPARENT } from '../../constants.js'; import { EFFECT_TRANSPARENT } from '../../constants.js';
import { branch, render_effect } from '../../reactivity/effects.js'; import { branch, block, destroy_effect } from '../../reactivity/effects.js';
/** /**
* @template {(node: import('#client').TemplateNode, ...args: any[]) => import('#client').Dom} SnippetFn * @template {(node: import('#client').TemplateNode, ...args: any[]) => import('#client').Dom} SnippetFn
@ -12,13 +12,19 @@ export function snippet(get_snippet, node, ...args) {
/** @type {SnippetFn | null | undefined} */ /** @type {SnippetFn | null | undefined} */
var snippet; var snippet;
var effect = render_effect(() => { /** @type {import('#client').Effect | null} */
var snippet_effect;
block(() => {
if (snippet === (snippet = get_snippet())) return; if (snippet === (snippet = get_snippet())) return;
if (snippet) { if (snippet_effect) {
branch(() => /** @type {SnippetFn} */ (snippet)(node, ...args)); destroy_effect(snippet_effect);
snippet_effect = null;
} }
});
effect.f |= EFFECT_TRANSPARENT; if (snippet) {
snippet_effect = branch(() => /** @type {SnippetFn} */ (snippet)(node, ...args));
}
}, EFFECT_TRANSPARENT);
} }

@ -7,7 +7,7 @@ import { should_intro } from '../../render.js';
import { is_function } from '../../utils.js'; import { is_function } from '../../utils.js';
import { current_each_item } from '../blocks/each.js'; import { current_each_item } from '../blocks/each.js';
import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../constants.js'; import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../constants.js';
import { BLOCK_EFFECT, EFFECT_RAN } from '../../constants.js'; import { BLOCK_EFFECT, EFFECT_RAN, EFFECT_TRANSPARENT } from '../../constants.js';
/** /**
* @template T * @template T
@ -241,18 +241,26 @@ export function transition(flags, element, get_fn, get_params) {
(e.transitions ??= []).push(transition); (e.transitions ??= []).push(transition);
// if this is a local transition, we only want to run it if the parent (block) effect's // if this is a local transition, we only want to run it if the parent (branch) effect's
// parent (branch) effect is where the state change happened. we can determine that by // parent (block) effect is where the state change happened. we can determine that by
// looking at whether the branch effect is currently initializing // looking at whether the block effect is currently initializing
if (is_intro && should_intro) { if (is_intro && should_intro) {
var parent = /** @type {import('#client').Effect} */ (e.parent); let run = is_global;
// e.g snippets are implemented as render effects — keep going until we find the parent block if (!run) {
while ((parent.f & BLOCK_EFFECT) === 0 && parent.parent) { var block = /** @type {import('#client').Effect | null} */ (e.parent);
parent = parent.parent;
// skip over transparent blocks (e.g. snippets, else-if blocks)
while (block && (block.f & EFFECT_TRANSPARENT) !== 0) {
while ((block = block.parent)) {
if ((block.f & BLOCK_EFFECT) !== 0) break;
}
}
run = !block || (block.f & EFFECT_RAN) !== 0;
} }
if (is_global || (parent.f & EFFECT_RAN) !== 0) { if (run) {
effect(() => { effect(() => {
untrack(() => transition.in()); untrack(() => transition.in());
}); });

@ -230,9 +230,12 @@ export function render_effect(fn) {
return create_effect(RENDER_EFFECT, fn, true); return create_effect(RENDER_EFFECT, fn, true);
} }
/** @param {(() => void)} fn */ /**
export function block(fn) { * @param {(() => void)} fn
return create_effect(RENDER_EFFECT | BLOCK_EFFECT, fn, true); * @param {number} flags
*/
export function block(fn, flags = 0) {
return create_effect(RENDER_EFFECT | BLOCK_EFFECT | flags, fn, true);
} }
/** @param {(() => void)} fn */ /** @param {(() => void)} fn */

Loading…
Cancel
Save