Thursday, April 22, 2021

Updated February 18th 2022 to reflect new features of Vue 3 and stuff I've learned from experience in the meantime.

I have been using Vue.js for client side browser stuff for a few years and I am very happy with it. However, I have been curious about Svelte for a while, and as I had an idea for a new project where Svelte might be a better fit than Vue, I decided to take a closer look.

Also, I have a number of ongoing projects in Vue 2, and with the significant breaking changes in Vue 3, now might be a good time to explore alternatives, as moving to a different framework might not be much more work than the Vue upgrades that I am facing anyway.

The following is based on my own experience with Svelte and Vue:

In favor of Svelte

  • No runtime
    The Vue runtime is not that big, but still significant for smaller "apps".
    Consider, for example, the code to validate a simple contact form. Here the Vue runtime would be disproportional huge for the functionality provided.
    Small Svelte apps compile to just a few kBs and need no runtime.

  • Two-way property binding
    In Svelte this is simple (bind:propname={variable}) which I found very convenient. In Vue.js it requires emitting events and more code.

  • Terser and more readable attribute value binding
    With Svelte, you can interpolate dynamic values anywhere in an attribute value using {...}:

    <a href="/items?type={varType}&page={varPage}">
    

    With Vue.js, the attribute name must be prefixed with v-bind: or : (shorthand), and the entire attribute value is then evaluated as JavaScript:

    <a :href="'/items?type=' + varType + '&page=' + varPage">
    
  • Simpler to declare reactive variables
    In Svelte you simply declare a variable in the script root (like let x=0) and it is automatically reactive.
    In Vue, for a variable to be reactive, it must either be defined as a property on the data object (Options API), or be created using the ref() or reactive() function (Composition API).

  • Simpler to declare props
    In Svelte you simply declare a variable in the script root and prefix it with export (like export let x=0).
    In Vue, to make a component property, you need to define it as a property on the props object (Options API), or through the defineProps() method (Composition API).

  • $: label
    The Svelte $: label makes the following script block re-run anytime a reactive variable used within that block changes.
    This is similar to Vue computed and watch blocks, but simpler, more convenient, and much terser syntax.

  • Raw html rendering not tied to an HTML element
    Svelte: {@html HtmlString}
    Vue: <div v-html="HtmlString"></div>

  • No this. this. this. / .value .value .value
    Unlike Vue, in Svelte you don't need to prefix everything with this. in code blocks to get at anything else within the same component.
    This is also a constant cause of errors in Vue for me. Template in-line script does not need this, and so whenever you move code between template and code blocks and forget to fix this - boom.
    In Vue 3, if you use the Composition API, you can avoid this. inside the "setup" function. But you still have to qualify access to reactive variable values like Refs - so this.VarName becomes VarName.value - not much better.

  • Performance
    Svelte is faster at updating the UI - supposedly because it doesn't use a "virtual DOM" (like Vue, React, Angular, etc.).
    Using browser performance tools - the measured difference is significant.
    Without such tools - it is hard to tell the difference - Vue is certainly fast enough.
    I imagine this being an advantage when coding for low powered devices.

In favor of Vue

  • "Deep" reactivity
    In Vue.js, when using the data option or the reactive() function, a JavaScript object is transformed into an object where each individual property (including those on nested objects) is reactive. Each property in effect becomes its own "store".
    This is very convenient and simple to work with.
    In Svelte, variables declared at a script block root are reactive (based on assignment) and so are explicitly defined "stores". But it is not "deep", meaning that assigning a value to a leaf node on a larger object, will trigger re-calculation/re-rendering based on an assumption that the whole object changed.

  • Client side template compilation
    Smaller Vue "apps" can be included as source on a web-page directly without any pre-processing / build tools.
    For web-pages (not "apps") where you need just a little bit of reactivity (like order forms), this is perfect. No need to run a compiler/bundler etc.
    Another cool thing about this is, that it allows you to put dynamically server side rendered html/data directly inside a Vue template, mixing server and client side processing very nicely.
    I have personally used this quite a lot, and the beauty of this was exactly the thing got me started with Vue in the first place.
    There is a special version of Vue optimized for above scenario - Petite-Vue. Another similar option for this is Alpine.js.
    This is not possible with Svelte. Svelte apps must always be compiled with a build tool.

  • Ecosystem
    Vue is more established and enjoys a much larger selection of component libraries, StackOverflow answers, blogs posts, etc.

Tie

  • Single file components (SFC)
    Both have this - which is just awesome.

  • Great documentation web-sites
    Both have this.

  • Zero indentation / no curly brace mess
    In both Svelte and Vue, you can write code at zero indentation enclosed by no curly braces, making code clean and easy to read.
    Vue 2 required at least 2-3 levels of indentation before you write any actual program code, but this was "fixed" in Vue 3 with the <script setup> feature.

  • Multiple root elements in components - a.k.a. "fragments"
    Both support this.
    In Vue 2, you could only have one root element, but this was "fixed" in Vue 3.

  • Browser DevTools
    Browser (Chrome/Firefox) "DevTools" are available for both Svelte and Vue.js, and with both tools, you can browse the live component hierarchy and see and change component property values.

Bundle size matters

Bundle size for small apps is certainly smaller with Svelte because there is no runtime.

But the bundle size grows faster for Svelte apps than for Vue apps - because the Svelte compiler adds stuff to the code (mainly for reactivity), while with Vue, code is largely left as is.

It appears that at some point around "medium sized" app, compiled Svelte apps could become bigger than Vue apps including runtime.

I recently did a small SPA (Danish budget calculator) based on Vue, and figured that it would be a nice test to convert this to Svelte. I copied the .vue files to a new Svelte project, renamed the files .svelte, and then manually massaged them into the Svelte syntax. The source code (Vue + Svelte) is available at: https://github.com/jesperhoy/Mit-Budget.dk

Minified and gzipped, the javascript for the original Vue version (https://mit-budget.dk/vue) is 9.2kb + 23.6kB Vue runtime = 32.8kB total (Note: this was based on Vue 2 - the Vue 3 runtime is significantly larger).
The Svelte version (https://mit-budget.dk/svelte) is 19.2kB.

Conclusion

Rich Harris asked the Marie Kodo inspired question "Does this code spark joy?"

Back when I discovered Vue - this really was a game changer for me - and it did spark a lot of joy :-)
I imagine many programmers feel this way when they first "get" the reactive UI model (be it in Vue, React, Angluar, etc.).

Vue still sparks joy, but Svelte does so even more :-)

I especially like the simpler and terser Svelte syntax, and not having to distribute a runtime.

Share: Twitter /  Facebook /  LinkedIn    
Copyright © 2006-2022 Jesper G. Høy