Skip to content

Improve tree-shaking for unused hydration/transition code in Svelte 5 #9533

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

Closed
nolanlawson opened this issue Nov 18, 2023 · 5 comments · Fixed by #10497
Closed

Improve tree-shaking for unused hydration/transition code in Svelte 5 #9533

nolanlawson opened this issue Nov 18, 2023 · 5 comments · Fixed by #10497
Milestone

Comments

@nolanlawson
Copy link
Contributor

Describe the problem

First off: I've been using Svelte since v1, and I really admire the tool and the team! The work on the Svelte 5 rewrite is particularly impressive, especially with so few breaking changes.

My issue is that I'm using Svelte to build a standalone component: emoji-picker-element. I'm using Svelte because it's one of the most lightweight frameworks in terms of bundlesize, especially for authoring a single component.

When upgrading from Svelte 4 to Svelte 5, I noticed that the bundle size increased by 20% compressed (42.8kB -> 51.2kB), even though I adopted snippets to reduce some duplication. The main cause of the new increase seems to be transition and hydration code, which I'm not using for this component. (nolanlawson/emoji-picker-element#376)

Previously I had been using some small hacks to remove unused code (e.g. replacing options.hydrate with false), but with Svelte 5, these techniques don't seem to work anymore. And unfortunately the new code is much more tightly-integrated into the Svelte framework code, so it's difficult to remove using simple string replacement.

Describe the proposed solution

I guess my request is:

  1. If a component is not using transition code, then it should be possible to tree-shake transition-related code somehow.
  2. Ditto for hydration code.

Alternatives considered

To be sure, this is not a must-have feature. And someone building a large app with hundreds of components is not going to care about a one-time bundle size cost. But I think it would be great for Svelte to be useful for portable, single components (ideal for the custom element build target!).

I'm also excited that the changes in Svelte v5 should theoretically allow for even more bundlesize savings (HTML strings instead of manual DOM operations, plus snippets to reduce duplication).

In any case, thanks for considering it!

Importance

nice to have

@trueadm
Copy link
Contributor

trueadm commented Nov 19, 2023

What Svelte logic blocks do you use in your component? I want to better understand how we can tree-shake here, as Svelte 4 works completely differently from Svelte 5 – for very good reasons. We tried to unify hydration so people hydrating didn't pay the cost of have components with two-paths which caused code-size issues.

Transitions are another story entirely. Svelte 4 actually pulls in all the same transition triggering logic, because transitions can potentially be global. However, I imagine the logic we have in Svelte 5 might be slightly larger – can you share the code that would improve code sizings significantly?

@nolanlawson
Copy link
Contributor Author

Thanks for the response!

What Svelte logic blocks do you use in your component?

You can see which Svelte logic blocks I'm using here. I'm only using #each, #if, #else, and #snippet.

can you share the code that would improve code sizings significantly?

I did try some extremely hacky string replacement to remove unused code:

      // remove hydration
      'current_hydration_fragment !== null': 'false',
      'current_hydration_fragment === null': 'true',
      'hydration_fragment === null': 'true',
      'hydration_fragment !== null': 'false',
      'get_hydration_fragment(first_child)': 'null',
      // remove transitions
      'active_transitions.length': '0',
      'alternate_transitions.size': '0',
      'consequent_transitions.size': '0',
      'transitions.size': '0',
      'each_block.transitions': '[]',
      'block.transitions': 'null',
      "trigger_transitions(transitions, 'key', from)": '',
      "trigger_transitions(transitions, 'out')": '',
      '= each_item_transition': '= () => {}',

Unfortunately it barely moved the needle – 15% regression instead of 20% – and as you can see, it's much gnarlier than just replacing options.hydrate.

I know it's not a minimal repro, but if you wanted to play with the component directly, you could check out the svelte-5 branch in that project, run yarn, and then inspect the picker.js file. yarn dev will also provide a live view for debugging, which I used with the Chrome DevTools Coverage tool to determine what's unused.

Thanks again for considering this! I understand there are a lot of tradeoffs here and it's not easy to optimize for all use cases.

@benmccann benmccann added this to the 5.0 milestone Nov 24, 2023
@trueadm
Copy link
Contributor

trueadm commented Nov 25, 2023

@nolanlawson We've pushed a bunch of improvements, would you mind checking out the latest alpha of Svelte 5 and letting us know if the code size has improved for you? I think there's going to be some additional weight we can't get away from here too – we now need the signals runtime, which is overhead you can't avoid. Hopefully, some of our changes have improved the other parts though!

@nolanlawson
Copy link
Contributor Author

Very nice!! The size difference for me is down to +13% (+5.73 kB). Before, it was +8.4kB, so it's almost 50% improved! (And this is without any hacks on my part.)

@trueadm
Copy link
Contributor

trueadm commented Nov 26, 2023

We might be able to shave some more bytes off if we can have another entry point for hydration. However, that’s likely not going to be huge amounts. Svelte 5 does gzip better than Svelte 4 though!

dummdidumm added a commit that referenced this issue Feb 16, 2024
Introduces a new `hydrate` method which does hydration. Refactors code so that hydration-related code is treeshaken out when not using that method.
closes #9533
part of #9827
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 a pull request may close this issue.

3 participants