Skip to content

feat: compiler-driven each block reactivity #12744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Aug 12, 2024
Merged

Conversation

Rich-Harris
Copy link
Member

@Rich-Harris Rich-Harris commented Aug 6, 2024

Currently, we don't know at compile-time whether or not an each block item is wrapped in a source. Consequently, we wrap items in $.unwrap — something like this...

{#each [1, 2, 3] as n}
  <p>{n}</p>
{/each}

...becomes this:

$.each(node, 1, () => [1, 2, 3], $.index, ($$anchor, n, $$index) => {
  var p = root_1();
  var text = $.child(p);

  $.reset(p);
  $.template_effect(() => $.set_text(text, $.unwrap(n)));
  $.append($$anchor, p);
});

There's several things about this that are suboptimal:

  • It's extra work in a hot code path — two extra function calls (unwrap and, in turn, is_signal) per access
  • Because of our flawed code generation, the calls are often doubled up ($.unwrap($.unwrap(...)))
  • is_signal is buggy. Duck typing is very rarely a good idea
  • items are wrapped even when (as in the case above) it's clear that the value won't change. The only time items aren't wrapped is when the key and the item are identical ({#each items as item (item)}), and even then it will become wrapped if some (possibly brittle) runtime checks aren't satisfied. These checks stand in the way of landing breaking: change $state.frozen -> $state.raw #12742

A better route is to determine at compile time whether we need to wrap items. This does mean sacrificing the 'key is item' optimisation (though perhaps there are cases where we could reinstate it? It might just be stores that break it, because they're mutable even in runes mode) but I suspect this case is somewhat rare. In return we optimise other cases (like each [1, 2, 3]), and every property access gets microscopically faster. (We also shrink the runtime a tiny amount.)

Draft because this revealed an issue with the getters mechanism — bindings aren't shadowed. Might fix that in a separate PR first. #12780

Before submitting the PR, please make sure you do the following

  • It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • Prefix your PR title with feat:, fix:, chore:, or docs:.
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.

Tests and linting

  • Run the tests with pnpm test and lint the project with pnpm lint

Copy link

changeset-bot bot commented Aug 6, 2024

⚠️ No Changeset found

Latest commit: 7e106e6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@Rich-Harris Rich-Harris mentioned this pull request Aug 10, 2024
9 tasks
@Rich-Harris
Copy link
Member Author

I reinstated the 'key is item' optimisation. This does come with a caveat — it will result in incorrect behaviour if a legacy mode component passes mutable data to a runes mode component that then uses the optimisation. I think this trade-off is probably acceptable.

@Rich-Harris Rich-Harris marked this pull request as ready for review August 11, 2024 11:43
@Rich-Harris Rich-Harris merged commit a5d349e into main Aug 12, 2024
9 checks passed
@Rich-Harris Rich-Harris deleted the each-block-reactivity branch August 12, 2024 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants