sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
78.21k stars 4.09k forks source link

{#each} with immutable values #7363

Closed adiguba closed 9 months ago

adiguba commented 2 years ago

Describe the problem

The "update" method of {#each} blocks is not optimized for immutable object.

When updating a single element of the array, all the elements of the array are reinterpreted (even if the DOM is not modified).

For exemple if we have something like this :

<ul>
{#each names as name}
    <li>{name.toUpperCase()}</li>
{/each}
</ul>

It will produce the following update method for the each-block :

p(ctx, dirty) {
    if (dirty & /*names*/ 1 && t_value !== (t_value = /*name*/ ctx[4].toUpperCase() + ""))
        set_data(t, t_value);
},

As a result, the method toUpperCase() will be called on all elements of the array, even if they have not been modified.

Another exemple with this REPL that show an array of 10 names displayed via {#each}, and editable via an . Each edition generates 10 calls to the format() method...

REPL : https://svelte.dev/repl/49cf048b227647deb9fd46bab489dbd0?version=3.46.4

These examples are simplistic and do not cause problems, but on larger arrays this could cause performance issues...

Describe the proposed solution

It would be nice to be able to tell {#each} that we are using immutable objects.

For exemple by using a keywork like immutable :

{#each immutable names as name}
    <li>{name.toUpperCase()}</li>
{/each}

Or by using a new template syntaxe like {#lazyeach} or other :

{#lazyeach names as name}
    <li>{name.toUpperCase()}</li>
{/lazyeach}

It will produce the following update method, with a location on dirty which indicates if the current index has been modified

p(ctx, dirty) {
    if (dirty & /*names[i] was modified*/ 9999 && t_value !== (t_value = /*name*/ ctx[4].toUpperCase() + ""))
        set_data(t, t_value);
},

This way the each-block will do nothing at-all if the value has not been modified, without having to regenerate the values to control them.

Alternatives considered

Using a child <Component> with <svelte:options immutable/>, but this moves the logic to a child component, while the data is modified in the parent.

Importance

nice to have

adiguba commented 9 months ago

useless with Svelte5