Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Is Prepack like Svelte?

Note: I'm not involved in Prepack in any way — please correct me if I say anything incorrect below!

A few people have asked me if Prepack and Svelte are similar projects with similar goals. The answer is 'no, they're not', but let's take a moment to explore why.

What is Prepack?

Prepack describes itself as a 'partial evaluator for JavaScript'. What that means is that it will run your code in a specialised interpreter that, rather than having some effect on the world (like printing a message to the console), will track the effects that would have happened and express them more directly.

So for example if you give it this code...

[ 'alice', 'bob', 'carol' ].forEach( function ( person ) {
  console.log( `Hello ${person}!` );
});

...it will turn it into this:

console.log("Hello alice!");
console.log("Hello bob!");
console.log("Hello carol!");

That's amazing. It's a mind-bending project with all sorts of ramifications that we probably haven't thought of yet.

What is Svelte?

Svelte is a UI framework (like React or Vue), except that it's actually a compiler. It takes your UI templates and turns them into small, blazing fast components. In other words, rather than you providing new state and the framework doing a whole load of virtual DOM reconciliation to figure what changed, Svelte writes straightforward code that manipulates the DOM directly — for example, here's a snippet from the <h1>Hello {{name}}!</h1> example:

// the update method is called whenever you set new state
update: function ( changed, state ) {
  if ( text_1_value !== ( text_1_value = state.name ) ) {
    text_1.data = text_1_value;
  }
}

They sound quite similar!

At a high level, they do have something in common — they're both taking work that used to happen n times in the browser, and doing it once at build time instead.

But there is a crucial difference. Because Prepack is running your code, it only cares about what happens during initialisation. Take this example of what happens if you run Prepack on a 928 line Webpack bundle generated from the following code:

console.log(require('util').inspect({ hello: 'world!' }));

It collapses those 928 lines into this:

console.log("{ hello: 'world!' }");

That really is remarkable. But if instead of logging the output, we create a function to do the same — in other words, move the work from initialisation to later, when our app is already running...

window.log = function () {
  console.log(require('util').inspect({ hello: 'world!' }));
};

...the 928 lines in fact become slightly larger (although the resulting code does appear to initialise slightly quicker, as we'd expect).

In other words, while Prepack is magical, it isn't magic — it won't rewrite your app to make slow code run faster, it will only do initialisation work ahead of time.

What is Svelte doing differently?

Svelte isn't running your code, it's understanding it. That's how it's able to write code that starts fast and stays fast.

But Svelte is focused on doing one job — being a UI framework — whereas Prepack is a far more general purpose tool. The two projects occupy very different spaces, but are both part of a growing trend that's about moving work out of the browser and into servers and build processes. There's a huge amount of exploration still to do in this area, and for my money it's one of the more exciting topics in the JavaScript community at the moment.

olstenlarck commented May 11, 2017

Both are fantastic, by idea! Svelte is terrific, but when I tried it I just realized that it is, indeed, amazing, but also it generates a very large duplicated bytes. I'm talking about that it creates a factory for each component - that's totally okey, but when you have multiple components and you look over the code you see that these all components have almost exactly the same factory with just different name. So, there's more places for optimization, imo. :)

For proof of what i'm saying is that sample example - charpeni/svelte-example: notice that part of the generated "main" bundle

CatList.prototype._set = function _set(newState) {
  var oldState = this._state
  this._state = Object.assign({}, oldState, newState)

  dispatchObservers(this, this._observers.pre, newState, oldState)
  if (this._fragment) this._fragment.update(newState, this._state)
  dispatchObservers(this, this._observers.post, newState, oldState)

  this._flush()
}

CatList.prototype.teardown = CatList.prototype.destroy = function destroy(
  detach
) {
  this.fire('teardown')

  this._fragment.teardown(detach !== false)
  this._fragment = null

  this._state = {}
  this._torndown = true
}

Cat.prototype._set = function _set(newState) {
  var oldState = this._state
  this._state = Object.assign({}, oldState, newState)

  dispatchObservers(this, this._observers.pre, newState, oldState)
  if (this._fragment) this._fragment.update(newState, this._state)
  dispatchObservers(this, this._observers.post, newState, oldState)
}

Cat.prototype.teardown = Cat.prototype.destroy = function destroy(detach) {
  this.fire('teardown')

  this._fragment.teardown(detach !== false)
  this._fragment = null

  this._state = {}
  this._torndown = true
}

The only difference is the names (mainly) of the components and that this._flush() call in the set in some cases. In teardown / destroy there's no diff. So probably more better would be to have a factory for the factory of components. The constructors are also the same and all that is just for one simple demo application, generated large duplicated code.

So in short: I love Svelte, it's looks awesome, but not feels that good. Specifically for me, when i see such things, I can't consider using it. :)

kazzkiq commented May 12, 2017

@tunnckoCore Indeed, it would be great if Svelte had an option to share the same factory/common code with its components.

Something like shareCommons: true, etc.

@kazzkiq It would be nice, although the extra download size can be easily be mitigated by serving gzipped versions of your scripts. The 1.14kB of text in the example @tunnckoCore provided goes down to just 311B when gzipped, without any minification. The savings on a larger app would be even greater.

iddan commented Jun 15, 2017

Great comparison. Well done 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment