Svelte

Ankush Chadda
3 min readSep 3, 2019

--

Source — thinking reactivity — https://www.youtube.com/watch?v=AdNJ3fydeao

Key points — Svelte calls itself as a compiler, It compiles the to vanilla js, and without the code which is not used by the program. Its also fast and obviously small code footprint.

Reactive programming

Its essence is that at the time of declaration, we declare dynamic behavior of a value.

Eg — In a spread sheet , we define rules for each cell.If value of one cell changes, it may also change another value depending on the behavior rules.

Comparison with React

React introduced virtual DOM, however it does a lot of work to compare changes with the actual DOM.
We know that virtual DOM is not fast enough because it has to reload the method when any of the prop or state changes itself. It provides following to avoid too many reloads/faster reloads —

  • Abstraction leaks like ShouldComponentUpdate, PureComponenet, useMemo etc
  • Amortization like ConcurrentMode ( not sure what it means right now )

Triggering Updates in Svelte

In old svelete — it used similar syntax as React — this.set ( or this.setState in React)

In newer versions of Svelte, it uses assignment operator = (Vue.js figured it out first) to know what value has changed.

In a simple svelte code of -

<script>
let name = ‘world’;
let count = 0;
</script>
<h1>Hello {name}</h1>
<input bind:value=”{name}” >
<button on:click={() => count +=1 }>
Clicks: {count}
</button>

We see there are 2 updates happening with user actions —

  • In the input text value
  • In the count integer value

Svelte changes this code to —

/* App.svelte generated by Svelte v3.7.1 */
import {
SvelteComponent,
append,
detach,
element,
init,
insert,
listen,
noop,
run_all,
safe_not_equal,
set_data,
space,
text
} from “svelte/internal”;
function create_fragment(ctx) {
var h1, t0, t1, t2, input, t3, button, t4, t5, dispose;
return {
c() {
h1 = element(“h1”);
t0 = text(“Hello “);
t1 = text(ctx.name);
t2 = space();
input = element(“input”);
t3 = space();
button = element(“button”);
t4 = text(“Clicks: “);
t5 = text(ctx.count);
dispose = [
listen(input, “input”, ctx.input_input_handler),
listen(button, “click”, ctx.click_handler)
];
},
m(target, anchor) {
insert(target, h1, anchor);
append(h1, t0);
append(h1, t1);
insert(target, t2, anchor);
insert(target, input, anchor);
input.value = ctx.name; insert(target, t3, anchor);
insert(target, button, anchor);
append(button, t4);
append(button, t5);
},
p(changed, ctx) {
if (changed.name) {
set_data(t1, ctx.name);
}
if (changed.name && (input.value !== ctx.name)) input.value = ctx.name; if (changed.count) {
set_data(t5, ctx.count);
}
},
i: noop,
o: noop,
d(detaching) {
if (detaching) {
detach(h1);
detach(t2);
detach(input);
detach(t3);
detach(button);
}
run_all(dispose);
}
};
}
function instance($$self, $$props, $$invalidate) {
let name = ‘world’;
let count = 0;
function input_input_handler() {
name = this.value;
$$invalidate(‘name’, name);
}
function click_handler() {
const $$result = count +=1;
$$invalidate(‘count’, count);
return $$result;
}
return {
name,
count,
input_input_handler,
click_handler
};
}
class App extends SvelteComponent {
constructor(options) {
super();
init(
this,
options,
instance,
create_fragment,
safe_not_equal,
[]
);
}
}
export default App;

The above code is big, but it shows how seemingly simple functionality like binding an input text to a variable , and a click counter can be tricky.

On close inspection of the output code, we see that Svelte creates a method called instance which leaves the code nearly the same, just adding $$invalidate method which will invalidate the result when the value changes, and also at the end of event loop.

Making Svelte Reactive

React is not really reactive because it reruns the method on every prop/state change. However, to make Svelte reactive, we need to have values / variables bound to another.

let a = 10;
$: b = a + 5;

here, b is defined as a bound variable.

Comparing React to Svelte in a simple ToDo example —

<script>
let todos =[
{done: false, text: 'eat'},
{done: false, text: 'sleep'},
{done: false, text: 'code'},
{done: false, text: 'repeat'},
];

function toggleDone(t) {
todos = todos.map(todo => {
if (todo == t) {
return {done: true, text:t.text}
}
return todo;
})
}
let hideDone = false;
$: filtered = hideDone ?
todos.filter(todo => !todo.done):
todos
$: showing = filtered.length;
</script>
<label>
<input type="checkbox" bind:checked={hideDone}/>
hide done
</label>
<p>
Showing {showing} out of {todos.length}
</p>
<ul>
{#each filtered as todo}
<li on:click={()=>toggleDone(todo)}>
{todo.done} {todo.text}
</li>
{/each}
</ul>

We can see that the bound variables ( filteredand showing) are changed to the following code in Svelte —

$$self.$$.update = ($$dirty = { hideDone: 1, todos: 1, filtered: 1 }) => {
if ($$dirty.hideDone || $$dirty.todos) { $$invalidate('filtered', filtered = hideDone ?
todos.filter(todo => !todo.done):
todos) }
if ($$dirty.filtered) { $$invalidate('showing', showing = filtered.length); }
};

Which means, Svelte understands dependency graph and works by first changing the depending variable before changing the dependent variable.

--

--