Skip to content

A more holistic compilation process #15116

@Ocean-OS

Description

@Ocean-OS

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:

import 'svelte/internal/disclose-version';
import * as $ from 'svelte/internal/client';

// insert functions not reliant on too much state here...

var root = $.template(`template stuff`);
// insert any other $.templates here...
export default function Component($$anchor, $$props) {
	let fragment = root();
        // insert component instance
        // insert template effects here
        $.append($$anchor, fragment);
}
// event delegation goes here, if necessary

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:

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();
    }
}

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:

  • If the component doesn't use any logic blocks, it might be eligible
  • If the component doesn't use any components, it might be eligible
  • If the component doesn't use any $deriveds or $effects, it might be eligible
  • If the component doesn't use many $states, it might be eligible
  • If the template of the component has very few elements, it might be eligible
  • If there aren't any bindings, actions (or other directives) or prop spreading, it might be eligible
  • If there aren't any $state proxies involved, it might be eligible

I'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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions