diff --git a/.changeset/slow-chefs-dream.md b/.changeset/slow-chefs-dream.md new file mode 100644 index 000000000000..9cd0de887b31 --- /dev/null +++ b/.changeset/slow-chefs-dream.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: improve each block fast-path heuristic diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js index 1330141a8e43..dc420fc805cc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/template.js @@ -2137,11 +2137,10 @@ export const template_visitors = { } // The runtime needs to know what kind of each block this is in order to optimize for the - // immutable + key==entry case. In that case, the item doesn't need to be reactive, because - // the array as a whole is immutable, so if something changes, it either has to recreate the - // array or use nested reactivity through runes. - // TODO this feels a bit "hidden performance boost"-style, investigate if there's a way - // to make this apply in more cases + // key === item (we avoid extra allocations). In that case, the item doesn't need to be reactive. + // We can guarantee this by knowing that in order for the item of the each block to change, they + // would need to mutate the key/item directly in the array. Given that in runes mode we use === + // equality, we can apply a fast-path (as long as the index isn't reactive). let each_type = 0; if ( @@ -2149,21 +2148,22 @@ export const template_visitors = { (node.key.type !== 'Identifier' || !node.index || node.key.name !== node.index) ) { each_type |= EACH_KEYED; + // If there's a destructuring, then we likely need the generated $$index + if (node.index || node.context.type !== 'Identifier') { + each_type |= EACH_INDEX_REACTIVE; + } if ( + context.state.analysis.runes && node.key.type === 'Identifier' && node.context.type === 'Identifier' && node.context.name === node.key.name && - context.state.options.immutable + (each_type & EACH_INDEX_REACTIVE) === 0 ) { - // Fast-path + // Fast-path for when the key === item each_item_is_reactive = false; } else { each_type |= EACH_ITEM_REACTIVE; } - // If there's a destructuring, then we likely need the generated $$index - if (node.index || node.context.type !== 'Identifier') { - each_type |= EACH_INDEX_REACTIVE; - } } else { each_type |= EACH_ITEM_REACTIVE; } @@ -2289,7 +2289,7 @@ export const template_visitors = { ) : b.literal(null); const key_function = - node.key && (each_type & 1) /* EACH_ITEM_REACTIVE */ !== 0 + node.key && ((each_type & EACH_ITEM_REACTIVE) !== 0 || context.state.options.dev) ? b.arrow( [node.context.type === 'Identifier' ? node.context : b.id('$$item')], b.block(