-
-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
A more holistic compilation process #15116
Comments
But are such components distributed standalone? I'm not sure I understand what you're trying to solve. If you have a single "big" component in your app that requires the internal imports, then it does not matter if hundreds of small (single element) ones also need the imports, since it does not make the bundle any bigger. |
I feel this would introduce way more code than it saves to properly handle hydration and to deal with the "special" component than it saves. Especially because when bundled those imports basically vanish because they are being imported by other components. Furthermore it feels like it would actually help in such small amount of components that I'm not sure is worth the additional maintenance and runtime overhead |
I agree with Paolo: too many imports are a feature, not a bug. They all are reused at any scale and keep the bundle from growing too quickly |
This is too vague to be actionable, so I'm going to close it. But before I do, I want to explain why things work the way they do. This example code is 468 bytes... var root = document.createElement('button');
export default function Counter($$anchor) {
let button = root.cloneNode();
let count = 0;
let click = () => {
button.textContent = `Count is ${++count}`;
}
button.addEventListener('click', click);
button.textContent = 'Count is 0';
$$anchor.before(button);
return () => { //callback to be called when the component is destroyed
button.removeEventListener('click', click);
button.remove();
}
} ...while this code weighs 466: import * as $ from 'svelte/internal/client';
var on_click = (_, count) => $.set(count, $.get(count) + 1);
var root = $.template(`<button> </button>`);
export default function App($$anchor) {
let count = $.state(0);
var button = root();
button.__click = [on_click, count];
var text = $.child(button);
$.reset(button);
$.template_effect(() => $.set_text(text, `Count is ${$.get(count) ?? ''}`));
$.append($$anchor, button);
}
$.delegate(['click']); So it's already smaller, in incremental terms, and that's before we start adding new places where Yes, the second example imports functions — Consider what you're losing when you use the naive approach of interacting with the DOM directly — no more event delegation (if this component is rendered many times, adding all those event handlers will have a noticeable impact on memory usage), no control over scheduling (doing all the DOM updates at the same time means you avoid layout thrashing), no ability to avoid updating the DOM at all in the case where work should be paused for some reason (which will become relevant very soon). You lose far more than you gain. There is a sense in which a more 'holistic' compilation process makes sense — it would be great if (for example) the compiler could know that a specific prop was only ever a static value, so that we could optimise the code generated for rendering its value — but I'm afraid this isn't it. |
Describe the problem
One important thing about component-based development is that the individual components can be any size; some can basically be your entire app, and some can just be a single styled element for reusability. As a result, some of these components have the potential to be very small and performant, with the help of a Sufficiently Smart Compiler. However, Svelte compiles every component to the same format:
In reality, not every component is large or complex enough to warrant the need for two module imports and a template; component libraries such as shadcn/ui encourage the use of small, often single element, styled components. While I do agree that Svelte's internal library and compiled code is optimized and performant, it isn't as performant as vanilla DOM operations, nor as small— a Svelte counter component compiled with default configuration through Vite is 11kB, while a counter component written in vanilla JS is less than half a kilobyte. Being a compiler, Svelte has the advantage of being able to determine the most efficient component code to emit, and to be able to use alternative methods to generate smaller code when necessary.
Describe the proposed solution
It would be pretty beneficial if Svelte could figure out if it would be more performant and smaller to emit vanilla JS without the internal library. For example, a counter component could easily be written in vanilla JS like so:
This code is not only smaller, but more performant, as it directly interacts with the DOM and removes the (albeit small) overhead of signals and effects. I won't try to sugarcoat the difficulty this would entail, but I will say that it could lead to more performant components and smaller bundles.
As the DOM might be difficult to efficiently directly compile to, there should be a strict criterion for a component for it to be compiled to this format. Here's a bikesheddable criteria for now:
$derived
s or$effect
s, it might be eligible$state
s, it might be eligible$state
proxies involved, it might be eligibleI'm being purposefully very vague here, as the compiler should be very careful when it chooses to compile to this format, and it'd probably be best if testing was done to see the effectiveness of this compilation target, in terms of performance, compiled output quality, and scaleability.
Importance
nice to have
The text was updated successfully, but these errors were encountered: