jookyboi
4/24/2020 - 1:19 PM

Svelte is really fast

Svelte is really fast

In my testing, Svelte performs exceptionally well.

I heard a bunch of claims from the creator of Svelte about its render speed. I wanted to know more and delved into the source code. After reviewing the source and asking the creator a few questions, I tried my hand at breaking it with the 10,000 Processes code.

This article goes into detail about Svelte v3. At the time of writing, the extent of my experience with Svelte is only its performance. Because of that, this won't be a run-down on how to build an app.

Svelte is faster than React

During my testing, Svelte performed well above my expectations. I ported my React 10,000 Processes code over to Svelte. Code here: https://svelte.dev/repl?version=3.1.0&gist=17a9b7ea0c0176b2e28d9b30e079e5ad

It hit a smooth framerate and rendered everything with ease. I pushed it further and found it stable at up to 50K processes, but at a much lower framerate. I couldn't even get my React example up to 25K without it freezing up.

Performance-wise, Svelte is faster than React. Maybe not in every situation, but definitely in the one I tested. That's really impressive and a tribute to the improvements the Svelte developers have made to DOM rendering.

Svelte is serious business. I haven't worked with a modern framework this fast in a while. If you need to support embedded devices or want to get some crazy speed out of your renderer, Svelte's a great choice. Rich Harris, the creator, made something killer. I've love to see this performance in other rendering libraries.

How does Svelte work?

Svelte is different from React. In React, the entire component reacts to changes in props or state. Svelte variables react to changes in only those variables, similar to Knockout observables.

It takes each variable in your code and does a React-like setState behind the scenes. See, what you look at when coding Svelte looks like normal JS; in fact, it looks like normal HTML with the JS in a script tag. Very Backbone right? But it's not.

You see, Svelte compiles that code into JavaScript. Just like JSX, it takes that HTML and turns it into JS too!

That's pretty wild. So how does it work?

You have code like this:

<div
  style="
    color: {color};
    left: {x}ch;
    position: absolute;
    top: {y}em;
  "
>
  {value}
</div>

And Svelte compiles it into this:

function create() {
  div = element("div");
  t = text(ctx.value);
  set_style(div, "color", ctx.color);
  set_style(div, "left", "" + ctx.x + "ch");
  set_style(div, "position", "absolute");
  set_style(div, "top", "" + ctx.y + "em");
  add_location(div, file, 47, 0, 754);
}

If you have a JS variables and then modify them like this:

<script>
  let previousColor
  let previousValue
  let timeoutId
  const queueUpdate = () => {
    if (
      previousColor === color
      && previousValue === value
    ) {
      return
    }
    previousColor = color
    previousValue = value
    clearTimeout(timeoutId)
    timeoutId = (
      setTimeout(
        () => addToQueue(id),
        getRandomTimeout(),
      )
    )
  }
</script>

This is what happens when Svelte compiles to JS:

let previousColor
let previousValue
let timeoutId

const queueUpdate = () => {
  if (
    previousColor === color
    && previousValue === value
  ) {
    return
  }

  $$invalidate('previousColor', previousColor = color)
  $$invalidate('previousValue', previousValue = value)

  clearTimeout(timeoutId)

  $$invalidate('timeoutId', timeoutId = (
    setTimeout(
      () => addToQueue(id),
      getRandomTimeout(),
    )
  ))
}

Notice the $$invalidate function? This is an internal feature of Svelte that gets compiled into your code. As you can see, it's not touching your code at all, it's just passing the returned value of your = into a function so Svelte can check for changes.

Why's it fast?

Svelte uses a model similar to but different from Knockout. Think of it like this. When you change a value, only that value gets updated in the DOM. Just like Knockout right? Aside from some shared syntax and concepts, that's about the extent of their similarities.

History

AngularJS has a watcher in the background checking $scope and $rootScope for changes. As you can probably tell, this is only as fast as the interval. Having that checking interval constantly searching for changes really slows down JS's one processing thread. And it's completely inefficient. You should be reacting to changes, not checking for them.

React improved on AngularJS (and other frameworks at the time) by creating a virtual DOM and rendering to that first before rendering to the actual DOM. With a virtual DOM, it could make a fairly cheap comparison between the virtual DOM and your rendered output. It finds any changes, and only render those changes.

Thing is, it's actually comparing the entire DOM tree, not just the part you changed. So as your app grows and renders more components, React slows down. The more changes you make and the faster you make them, the slower this comparison becomes.

At my last job, I made a small-scale production app that ran into virtual DOM performance problems. I had to memoize certain components and utilize shouldComponentUpdate to speed it up. Even still, it wasn't perfect. If only concurrent mode was ready at the time. While the diffing cost is minimal in most applications, it worsens the larger your component tree; especially on mobile.

In later versions of React, concurrent mode will go live. This splits up those virtual DOM comparisons into chunks which greatly speeds it up. Still, this is a hack and has some major side effects in certain use cases. I've also run into those, but only in testing.

Learning from React's mistakes

These problems are why Svelte does away with the virtual DOM entirely. It's fast because it only renders small changes to the DOM. While it still does do value comparisons, it only cares about the ones that changed, not the entire DOM tree.

I looked at the source code. As soon as a change occurs, that change renders to the DOM. Any affected children are also sent through the ringer and those changes are also rendered.

If another change occurs during this timeframe, it's added to the render queue and is processed in time. It's possible for new renders to come in that change old ones; although, I'm not sure exactly how Svelte deals with those situations.

Once all renders have completed, the render loop stops processing and goes back to sleep; waiting for changes. It's extremely efficient and renders as fast as the browser can handle. This is the key to Svelte's speed advantage.

Conclusion

Svelte is fast, but is it for you? That depends on your needs. One guy tweeted about some low-powered devices that were only usable with Svelte. I've been wondering about its mobile performance myself since I've experienced slowness with React.

Svelte is fairly new so there are currently few experts who know how to use it in a larger codebase. If you know more than I do, please feel free to comment below and get in contact with me. I'd love for someone to change my mind on this speedy framework.

I look at quite a few factors when hooking up a DOM renderer in my own projects:

  • How quickly can I get up 'n going?
  • How much do I have to learn to accomplish my goal?
  • How easy is it to debug and maintain over time?
  • Does it have hot-reloading?
  • Can I use my existing tools like Sublime Text?

React has a huge following, and I don't believe that'll change for the same reason AngularJS is still a thing. Svelte made a pretty large slam dunk with some big names in the JS community so it might take some of the marketshare. It's definitely something to keep in mind when looking at React alternatives.

As a side note, .svelte syntax is now supported on GitHub so you won't have anything to worry about there.

On the other hand, I'm a big user of Colorcoder for semantic highlighting in Sublime Text and there are definitely some issues to iron out.