[Translation] Real reactive programming in Svelte 3.0

[Translation] Real reactive programming in Svelte 3.0

The title of the article may seem a little shouting, however, like the Svelte framework itself and the ideas behind it. If you still do not know anything about Svelte, buckle up, now we rush towards the revolution.

Note that this is not a lesson to get started with Svelte. There is already an excellent step-by-step interactive guide from the Svelte team that will immerse you in the world of reactive programming.

Disclaimer: I’m not a rock star in programming and I don’t know everything. I'm just enthusiastic about new trends that happen every day, and I like when I can, talk about them - this is how this article appeared. Treat it critically and be sure to let me know if I wrote something ridiculous.

Okay, now let's dive into the topic!

But first, React

Before telling me why I think Svelte will break everything, let's take a look at this recent tweet from a man named Dan, and try to understand what he meant:

Hmm, why is it then called React?

Another caveat: this article is in no way intended to criticize React. I decided to use it as an example, because most people who read this article dealt with React at one time or another in their lives. Just now this is the best example for contrasting Svelte.

What did Dan mean, and how did that affect how we write the code now? To answer these questions, let me tell you in a simplistic way how React works under the hood.

When the React application is rendered, a copy of the DOM is placed in a structure called Virtual DOM . This virtual DOM mediates between your React code and what the browser displays in the DOM.

Then, when changing data (for example, after calling this.setState or useState ), React does a little work to determine which parts of the application need to be redrawn.

It compares the virtual DOM with the real one to determine what has changed since the data was updated. It then redraws only those parts of the DOM that have differences in the virtual DOM, eliminating the need to redraw the entire DOM every time for any change.

Everything happens very quickly, because updating a virtual DOM is much cheaper than a real one, and React updates only the necessary pieces of a real DOM. This article explains this process much better.

You probably noticed one feature. If you do not tell React that the data has changed (by calling this.setState or an equivalent hook), the virtual DOM will not change, and no response will follow from React (ta-dam! ).

That's what Dan meant when he said that React is not fully reactive. React relies on the fact that you independently monitor the data of your application and report on their changes. This adds to your work.

Ok, now about Svelte

Svelte is a completely new way to create web applications in an incredibly fast, efficient, and truly reactive way. And all this without using a virtual DOM and with fewer lines of code than would be needed in another framework or library.

This all sounds great, of course, but how does it differ from many other JavaScript libraries and frameworks? - you ask. I will tell.

1. True reactivity

Svelte is not a library. Svelte is not a framework. Rather, Svelte is a compiler that gets your code and produces native JavaScript that directly interacts with the DOM without the need for any intermediary.

Wait, wait, what? Compiler? Yes - compiler. And this is a damn good idea, I can not understand why it has not been so obvious so far. Then tell you why I think it is very cool.

Here is a quote from the Rich Harris report at the YGLF 2019 conference:

Svelte 3.0 takes the reactivity from the component's API into the language itself.

What does this mean? Well, we already know that React (and most other frameworks) require you to use the API to tell it about data changes (call this.setState or useState ) and write their virtual DOM.

The need to call this.setState in React (and other UI frameworks and libraries) means that the reactivity of your application is now tied to a specific API, without which it will know nothing about data changes at all.

Svelte takes a different approach.

His code execution was inspired by the way it was done in Observable . Instead of running the code from top to bottom, it is executed in topological order. Let's look at the code snippet below and analyze what it means to run it in topological order.

  1.  (() = & gt; {
 2. let square = & gt;  number = & gt;  number * number;
 4. let secondNumber = square (firstNumber);
 5. let firstNumber = 42;
 7. console.log (secondNumber);
 8.}) ();  

When this code is executed from top to bottom, an error will be displayed in line No. 4, because secondNumber is used for firstNumber , which has not yet been initialized.

If you run this code in topological order, there will be no errors. How so? The compiler will not run this code from top to bottom, but will consider all variables in the code and generate a dependency graph (that is, who depends on whom initially).

The extremely simplified compiler path, with the topological passage of our example, looks like this:

  1.  Does the new variable 'square' depend on any other variable?
  - No, I initialize it.

 2. Does the new variable 'secondNumber' depend on any other variable?
  - It depends on 'square' and 'firstNumber'.  I have already initialized 'square', but have not yet initialized 'firstNumber', which I will now do.

 3. Great, I initialized 'firstNumber'.  Now I can initialize 'secondNumber' using 'square' and 'firstNumber'
  - Do I have all the variables required to run the expression 'console.log'?
  - Yes, I run it.  

At first glance, it seems that the code works from top to bottom, but if you look closely, you will notice that it is not.

When the compiler reaches line 4, it detects that it does not have firstNumber , so it pauses further execution and scans all the code, looking for the initialization of this variable. Well, this is exactly what happens in line No. 5, so first line No. 5 is executed, and then the execution of the code will return to line No. 4 and go on.

Briefly: provided that the expression A depends on the expression B , the expression B will be executed first regardless of the order in which these expressions are declared.

So, how does this compare to how Svelte realizes its real reactivity? You can mark any JavaScript expression with a special tag . It looks like this: $: foo = bar . That is, all you need to do is add a label with the name $ before the expression foo = bar (in strict mode this will not work, if foo was not previously defined).

So, when Svelte sees any expression with the $: prefix, it knows that the variable on the left receives its value from the variable on the right. Now we have a way to bind the value of one variable to the value of another.

This is reactivity! Right now, we used part of the standard API of JavaScript itself to achieve real reactivity without having to bother with third-party APIs like this.setState .

Here’s how it looks in practice:

  1.//vanilla js
 2. let foo = 10;
 3. let bar = foo + 10;//bar is now equal to 20
 4. foo = bar//bar is still 20 (no reactivity)
 5. bar = foo + 10//now bar is 30

 6.//svelte js
 7. let foo = 10;
 8. $: bar = foo + 10;//bar equals 20
 9. foo = 15//here bar will be equal to 25, because it depends on the value of foo  

Please note that in this example we did not need to recalculate bar with the new value of foo , neither directly nor by re-executing bar = foo + 10 , or by calling an API method, like this.setState ({bar = foo + 10}) . This is done automatically.

That is, when you change foo to 15 , bar automatically updates to 25 , and you don’t you need to call no API to do this. Svelte already knows everything.

Part of the compiled Javascript code in the above example looks like this:

 2. function instance ($$ self, $$ props, $$ invalidate) {
 3. let foo = 10;//bar equals 20

 4. $$ invalidate ('foo', foo = 15)//here bar will be equal to 25, because it depends on the value of foo

 5. let bar;

 6. $$ self. $$. Update = ($$ dirty = {foo: 1}) = & gt;  {
 7. if ($ dirty.foo) {$$ invalidate ('bar', bar = foo + 10);  }
 eight. };

 9. return {bar};
 ten. }

Take your time to read on, study this code snippet. Slow down.

Notice that the update to foo happens before bar is announced? This is because the compiler analyzes the Svelte code in topological order, rather than line by line from top to bottom.

Svelte responds independently to changes in data. You do not need to keep track of what is changing and when - it happens by itself.

Note: At line 4, the value of bar will not be updated until the next iteration of the EventLoop loop clears all tails.

So, we no longer need to worry about manually updating the state when data changes. You can think all day about the logic of the application while Svelte is in the process of matching the UI with the current state.

2. Brevity

Remember, I previously wrote that Svelte allows you to do more by writing fewer lines of code? I will show a simple component in React and its equivalent in Svelte, and you will see for yourself:

17 lines of code against 29

These two applications are completely identical in functionality, but you can see how much code we needed to write in React.js - and don’t even ask me to do this in Angular.

< br/>

I am the oldest developer

Apart from the fact that the Svelte code is more pleasing to the eye, it is also much easier to understand because it has fewer structures. For example, we don’t need an event handler to update the value of a text field — simply bind.

Imagine that you are just starting to learn web development. Which code would be less clear to you? The one on the left, or the one on the right?

While this may seem obvious, it quickly becomes clear how much more useful to write fewer lines of code when you start building larger and more complex applications. I personally spent hours trying to understand how the large React component that my colleague wrote.

I truly believe that such a simplified API in Svelte will allow you to read and understand the code much faster, improving our overall productivity.

3. Performance

Well, we saw that Svelte is truly reactive and allows you to do more with less effort. How about performance? And how comfortable are applications written entirely in Svelte?

One of the reasons why React is so powerful is that it uses a virtual DOM to redraw the application's user interface in small pieces, eliminating the need to redraw the entire DOM every time something changes (which is really very expensive).

However, the disadvantage of this approach is that if the data of the component changes, the React will redraw not only the component itself, but all its children, regardless of whether they have changed or not. That is why there are API methods in React such as shouldComponentUpdate , useMemo , React.PureComponent , etc.

This problem will always occur when using a virtual DOM to draw the user interface when the state of the application changes.

Svelte does not use a virtual DOM, but how then does it solve the problem of redrawing DOM according to new state data? Well, let me again quote Rich Harris from his remarkable performance on YGLF:

Frameworks are not tools for organizing your code. These are tools for organizing your mind.

This thought led to the idea that the framework could be something that works even at the application build stage, thus eliminating the need for your code to have an intermediary in runtime. Thanks to this idea, Svelte became a compiler, not a framework.

This simple idea also explains why Svelte is very fast. Svelte compiles your code into efficient low-level Javascript, which interacts directly with the DOM. This is all fine, but how does Svelte solve the problem of re-rendering the entire DOM entirely when data changes?

The difference is in how traditional frameworks (like React) and Svelte find out if something has changed. We have already discussed that it is necessary to call the API method in React to inform it when the data changes. In the case of Svelte, it is sufficient to simply use the assignment operator = .

If the value of a state variable — say, foo —is updated using the = operator, Svelte, as we already know, will update all other variables that depend on foo .This allows Svelte to redraw only those parts of the DOM that somehow get their value from foo .

I will not describe the actual implementation of this process, because this article is already quite voluminous. But you can see how Rich Harris himself talks about it.

In conclusion

Svelte 3.0 - one of the best things that has appeared in the field of web development lately. Some may call it exaggeration, but I disagree. The concept of Svelte and its implementation allows us to create large applications, while sending less Javascript code to the user's browser.

It also allows applications to be more productive, more lightweight and at the same time it turns out code that is easier to read. So will Svelte be able to replace React, Angular or any other traditional UI framework in the near future?

While I answer - no. Svelte is too young compared to them, so he needs time to grow up, grow up and figure out some quirks that we do not even suspect yet.

Just as React has redefined the development of web applications with its appearance, Svelte also has the potential to change our understanding of frameworks and perhaps the whole process of thinking when creating applications.

Happy coding!

Source text: [Translation] Real reactive programming in Svelte 3.0