Skip to content
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

Whole-app optimisation #1102

Open
Rich-Harris opened this issue Jan 12, 2018 · 10 comments
Open

Whole-app optimisation #1102

Rich-Harris opened this issue Jan 12, 2018 · 10 comments
Labels
compiler Changes relating to the compiler feature request popular more than 20 upthumbs

Comments

@Rich-Harris
Copy link
Member

I keep bringing this up as a thing-we-should-do but it's probably time we had an issue for it with specific ideas about what it means and how to get there.

I'll kick things off with a few ideas of things we could do:

Static properties

<!-- App.html -->
<Greeting name='world'/>

<!-- Greeting.html -->
<h1>Hello {{name}}!</h1>

Right now, this involves creating three separate text nodes inside the <h1> (which we could collapse into one — Scott Bedard had some good ideas in Gitter), and adding update code that waits for state.name to change. We could replace all that with

h1.textContent = 'Hello world!';

Static computed properties

As a corollary to the above, if you know the values of the inputs to a computed property, and know that the computed property function is pure, you can precompute the value.

Collapsing entire components

A 'component' is really two things — the main fragment, and the interface. In a lot of cases, such as the <Greeting> component above, we don't actually need the interface — we can statically determine that there are no lifecycle hooks or events, and no way that the user could get a reference to the component.

Optimising styles

Component-level unused style removal is cool, but if we had all your styles we could start to do Styletron-style optimisations.


Will add to this list as other things occur to me; please suggest others!

@tivac
Copy link
Contributor

tivac commented Jan 12, 2018

This sort of fits in with the Static Properties section, but with modular-css-svelte I'm doing a really hacky thing to replace {{css.fooga}} reference with the modular-css output. It'd be pretty neat if static data replacements like that that were natively supported by the svelte compiler.

@Rich-Harris
Copy link
Member Author

Some half-baked thoughts on implementation:

The reason this is something of a tricky problem is that our tools are module-centric. The Svelte compiler operates on one component at a time, and module bundlers transform a single module at a time (the process of transformation from Svelte component/CSS file/whatever to JavaScript module, and the process of discovering the dependency graph, are linked — we generate JS, then we scan for import declarations).

Whole-app transformations do take place (minification and tree-shaking are two examples), but by the time we reach that stage, the deep knowledge of the structure of a component has been lost, and it's essentially impossible to go back and apply the aforementioned optimisations to the code that has already been generated.

I don't think Svelte is unique in this regard, and I sense that there's going to be a trend towards tools that operate more holistically. One idea I've been kicking around is changing Rollup's design so that graph discovery and transformation are separate:

function someCompiler(opts) {
  return {
    name: 'some-compiler-plugin-for-rollup',

    discover(code, id) {
      // ...the code is analysed...
      return {
        dependencies: ['./foo.js', 'Bar.html'],
        api: {
          reticulateSplines() {
            // this is a method that other modules could call in a later `transform`
            // function, to determine things about this module
          }
        }
      };
    },

    transform(code, id) {
      const capabilities = {};
      this.getDependents().forEach(module => {
        if (module.reticulateSplines()) {
          capabilities.reticulate = true;
        }
      });

      return compiler.compile(code, capabilities);
    }
  };
}

Example might be a bit abstract but you get the basic idea — we first do a discover pass, build the graph, then transform hooks are able to query their dependents to determine what capabilities the current module needs to expose.

(Existing plugins would continue to work — Rollup would just merge discover and transform, as it currently does.)

Meanwhile on the Svelte side, we would need to expose new functions that could do the necessary analysis ("draw the rest of the owl").

The nice thing about this is it's not a Svelte-specific solution. The bad thing is it is a Rollup-specific solution. (@TheLarkInn, feel free to ignore this but I'm tagging you in because we've talked about this briefly in the past and it's a place where bundlers should definitely be comparing notes.)


I was chatting to @guybedford about this stuff this week, and he had a suggestion that is a lot more down-to-earth: expose functions and let the treeshaker deal with it.

In other words, rather than not generating the update function for the {{name}} in the <Greeting> component above, we generate it but <App> doesn't import or call that function. I haven't quite wrapped my noggin around how that could work — it might not even be possible, and it's certainly less powerful (you couldn't generate h1.textContent = 'hello world!'), but it's definitely an appealing line of enquiry, given that treeshaking functions is something our tooling is getting pretty good at.

@arxpoetica
Copy link
Member

Said this in Gitter, but posting here for convenience:

stuff like FOUC and time to render child components and CSS transitions comes to mind
As far as a practical use case for app level optimization
Also: preloading async optimization and general other loading stuff

@Rich-Harris
Copy link
Member Author

Relevant: https://twitter.com/tomdale/status/953365543213518848

@thgh
Copy link
Contributor

thgh commented Feb 7, 2018

Only compute computed properties when used in view:
https://svelte.technology/repl?version=1.54.1&gist=4faf8a9862d75b3dec0b3a6079051643

Only compute computed properties when accessed:
https://svelte.technology/repl?version=1.54.1&gist=7b93771bb5f0e9eadb99051df0158c2d

Seems to work now, just a HMR bug? Only recompute computed properties which parameters have changed:
https://svelte.technology/repl?version=1.54.1&gist=61200957976c77c4a7283f3788b33569

@nolanlawson
Copy link
Contributor

In terms of whole-app optimization, some other ideas that spring to mind:

  • mangle veryLongPropNamesToWhichIAmPartial into a, b, c, etc
  • mangle method names, especially those used only inside the component (I do this all the time)
  • inline/flatten components when the child component isn't reused by multiple parents

Just some random ideas. :)

@benmccann
Copy link
Member

I don't use animations anywhere in my app. It'd be nice if the animation code were not included in that case

@swyxio
Copy link
Contributor

swyxio commented Jun 11, 2021

linking related issues:

@stale
Copy link

stale bot commented Dec 23, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale-bot label Dec 23, 2021
@dummdidumm dummdidumm added popular more than 20 upthumbs and removed stale-bot labels Dec 24, 2021
@Azarattum
Copy link
Contributor

Azarattum commented Apr 23, 2024

Any chance we could see something like this in Svelte 5?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler Changes relating to the compiler feature request popular more than 20 upthumbs
Projects
None yet
Development

No branches or pull requests

10 participants