📜 ⬆️ ⬇️

Magically vanishing JS framework

Oddly enough, but so far on Habré there is not a single article about this at least very interesting development tool. Perhaps this is due to the fact that this framework is quite young, as well as the idea behind it. Or maybe the whole thing in constant hype is kindling around the “big three” of the frontend and closing the review of alternative solutions. I do not know for sure, but I will try to correct this miscalculation in this article. Do not switch.

imageimage

Javascript without hitting a complexity wall. But a compiler can do it for you.

- Rich Harris, 2016

Prologue


I will come from afar. Since about 2013 and up to now, I actively use RactiveJS in my work - not very popular, but a developing reactive MVVM framework. At that moment, when Ractive appeared, it seems to me, it was very innovative and still has the most convenient, in my opinion, API. However, like any other tool, Ractive has its drawbacks. The main disadvantages for me are its size (~ 200Kb minified), and also not too high, compared to competitors, the speed of work .
')
Relatively recently, it became necessary to write a fairly interactive widget for sites (ala callback widget). The basic requirements for the widget are clear: it must weigh a little and work fast. Given the downsides that I described above, Ractive, as well as most other JS frameworks (Vue ~ 70Kb / React ~ 150Kb / etc.), are not very well suited for this task. Therefore, at some point, even the thought arose of writing a widget on vanilla JS, without any frameworks or libraries at all. However, I was lucky.

The magical disappearing UI framework


Svelte
So, SvelteJS is a JS framework that compiles its code in vanilla javascript at the build stage (AoT). In this connection, overhead costs associated with the framework itself completely disappear. Its creator and main maintainer is Rich Harris (Rich Harris) , the author of such cool things as Rollup , Buble , Roadtrip , as well as Ractive himself. In 2016, Rich stopped active work on Ractive, leaving it to other maintainers, in order to develop Svelte, and in the same year wrote an introductory article about his new development.

In his new “brainchild”, Rich tried to rethink why we generally use frameworks. Why are we ready to send to the user's browser hundreds and thousands of kilobytes of javascript, clinging to a bunch of abstractions over native code, in fact, killing performance. If you do not think this is a problem, read Alex Russell’s blog (Alex Russell) , there are quite a few interesting thoughts and examples.

So, Rich, came to the conclusion that the main task that frameworks solve is not to reduce the complexity of your own code, by putting unnecessary complexity into the framework code, but by structuring your thoughts and ways to solve problems. In other words, we don’t need a framework as such. We need only a way to write applications, guided by some approach, for example, component. And we need a convenient API for this. So, if the framework is only a means to write code, and not the application code itself?

Why is Svelte perfect for the task described?

When you write to React, Vue or Ractive, you have the code of your application, as well as the code of the framework itself, on which the code of your application is based, without which this code cannot work a priori. In total, we have the framework code (on average, 100kb) + the application code, which actually solves the tasks of the application. However, when you write to Svelte, you only have the code for your application, because Svelte is primarily a compiler that creates vanilla JS from code written using the Svelte API. The compiled code solves only the tasks of your application and has no overhead. Accordingly, the JS bundle weighs exactly as much as the code of the application itself.

Next, Svelte is quick. In fact, it is as fast as Javascript itself. And this is obvious, because Svelte is vanilla javascript, which you do not need to write.

In short, Svelte is a tool to write vanilla JS without having to write to vanilla JS. Here is such a tautology. )))))

Svelte features


image
So, at first glance, everything sounds pretty appetizing, but you need to take a closer look. Make it easy!

Svelte probably has the flatter learning curve of all the frameworks I’ve been writing on. In my case, it didn’t even require training, because the Svelte API is almost identical to the Ractive API, except that Ractive is still more sophisticated. However, Svelte is not far behind and has a couple of cool features in the arsenal, which I will discuss below.

All Svelte documentation is literally on one page (15 min reading), there is also a ready-to-use REPL with interactive code examples (including 7GUIs ).

The basis of the Svelte application is the components of a surprise surprise. In terms of their appearance and method of writing, svelte components are very similar to Vue single-file components or Riot tags , and indeed to what we have long been in almost all frameworks, even in Ractive . Usually, this entire farm is based on a loader for the Webpack or Rollup. With Svelte, the same story, there is svelte-loader and rollup-plugin-svelte . You can also always compile components into pure JS using svelte-cli .

So Svelte-components is just an html file, which can have the following structure:

<!--  html     --> <h1>Hello {{name}}!</h1> <!-- component scoped  --> <style> h1 { color: red; } </style> <!--   ,  ES6  --> <script> export default { }; </script> 

None of the items are required. Therefore, a simple stepper component can be written like this, without a script tag at all:

 <!-- stepper.html --> <button on:click="set({ count: count - 1 })">-</button> <input bind:value="count" readonly /> <button on:click="set({ count: count + 1 })">+</button> 

Svelte itself is extremely minimalistic. However, it actually has everything you need for developing web applications.

Components & composition


The imperative creation of a component instance looks like this:

 import MyComponent from './MyComponent.html'; const component = new MyComponent({ target: document.querySelector( 'main' ), data: {} }); 

Naturally, there is a composition of components and the transfer of values ​​through attributes:

 <div class='widget-container'> <Widget foo="static" bar="{{dynamic}}" /> </div> <script> import Widget from './Widget.html'; export default { data () { return { dynamic: 'this can change' } }, components: { Widget } }; </script> 

Also included is markup injection with slots (hi Vue):

 // Box component <div class='box'> <slot><!-- content is injected here --></slot> </div> // parent component <Box> <h2>Hello!</h2> <p>This is a box. It can contain anything.</p> </Box> 

Data & computed props


Svelte components have an internal state, which is described in the “data” profile and should be a POJO object. Like Ractive or Vue, a Svelte component can have computed props properties depending on other reactive data:

 <p> The time is <strong>{{hours}}:{{minutes}}:{{seconds}}</strong> </p> <script> export default { data () { return { time: new Date() }; }, computed: { hours: time => time.getHours(), minutes: time => time.getMinutes(), seconds: time => time.getSeconds() } }; </script> 

The only minus of calculated properties in Svelte is the lack of possibility to set a setter for them, i.e. calculated properties work only to get the value.

To work with the state, the Svelte component instance has built-in methods:

 component.set({ time: new Date() }); component.get('time'); 

When changing component data using the set () method, the change is reactively propagated to all dependent calculated properties and DOM.

Observers & Lifecycle hooks


As I already said, Svelte is minimalist, therefore it provides only 2 lifecycle hooks: oncreate and ondestroy . Changes in the data we can monitor with the help of observers:

 <script> export default { oncreate () { const observer = this.observe('foo', (newVal, oldVal) => {}); this.on('destroy', () => observer.cancel()); } }; </script> 


Events


Svelte has a full-fledged system of proxy events (DOM Events proxy) and custom events (Custom Events), which can also be applied to components:

 <!-- CategoryChooser.html --> <p>Select a category:</p> {{#each categories as category}} <button on:click="fire('select', { category })">select {{category}}</button> {{/each}} <script> export default { data() { return { categories: [ 'animal', 'vegetable', 'mineral' ] } } }; <!-- parent component --> <CategoryChooser on:select="doSomething(event.category)"/> </script> 

Here we subscribe to click on the button, convert the DOM click event to a custom event of the onselect component. Further, the parent component can subscribe to the onselect event of this component. In short, custom events also know how to “bubbling”. Everything is as usual.)))

The event API is also minimalistic:

 const listener = component.on( 'thingHappened', event => { console.log( `A thing happened: ${event.thing}` ); }); // some time later.. listener.cancel(); component.fire( 'thingHappened', { thing: 'this event was fired' }); 

Templating & directives


The syntax of Svelte templates is very reminiscent of "whiskers", but in fact they are not:

 <!--       ,     --> <h1 style="color: {{color}};">{{color}}</h1> <p hidden="{{hideParagraph}}">You can hide this paragraph.</p> <!--   --> {{#if user.loggedIn}} <a href='/logout'>log out</a> {{else}} <a href='/login'>log in</a> {{/if}} <!--    --> <ul> {{#each list as item}} <li>{{item.title}}</li> {{/each}} </ul> 

There are no custom directives in Svelte (at least for now), however there are several types of out-of-box directives:

 <!-- Event handlers --> <button on:click="set({ count: count + 1 })">+1</button> <!-- Two-way binding --> <input bind:value="count" /> <!-- Refs (like Vue) --> <canvas ref:canvas width="200" height="200"></canvas> <!-- Transitions --> <div transition:fly="{y:20}">hello!</div> 

I'm not sure that custom directives will appear at all. Yet it seems that this “AngularJS-style” approach is slowly becoming obsolete.

Custom methods & helpers


Component instance methods and helper functions for templates can be defined as follows:

 <script> export default { helpers: { toUppeCase: (str) => {} }, methods: { toast: (msg) => {} } }; </script> 

Plugins


Apparently in Svelte there are some developments in the plugin system. True documentation is silent about it. The same Transitions are implemented as separate modules and assembled into a separate svelte-transitions package. Obviously, you can write your own transitions.

There is also a separate package with additional methods for the component instance ( svelte-extras ), which can be loaded separately, bringing the Svelte API closer to the same Ractive:

 ractive.set('list.3.name', 'Rich'); //  set()       svelte.setDeep('list.3.name', 'Rich'); 

In general, the plugin system is not yet very advanced, or in any case described in the documentation. Zhdems.

SSR


Server rendering is also available, although it works quite strangely, compared to the same Ractive:

 //      Ractive const html = ractive.toHTML(); //     Svelte require( 'svelte/ssr/register' ); const html = svelte.render( data ); // css      const styles = svelte.renderCss(); 

State management


Svelte has its own interpretation of the storage mechanism, which on the one hand has some similarities with Redux / Vuex, but there are still more differences. You can read more in the documentation, but the main difference is that Svelte Store focuses not on the state change mechanism (immobility, one-way data flow and all that), but on the state exchange (sharing) mechanism between components. For example, using “store” instead of “data” in the top-level component will result in the data storage being automatically accessible to all child components. Honestly, I have not had a chance to use this mechanism yet, but as far as I understand, this is something like a global state within the same hierarchy of components, and not the entire application.

Also in the Svelte Store there are no actions and commits. However, if such an access control mechanism is needed, it is permitted to extend the Store class in order to implement such logic.

Bonus


image

Above, I briefly described the main features of Svelte. Considering its minimalism, as well as the actually built-in tree-shaking (only what is actually used is always compiled), the possibilities of Svelte do not look very bad.

However, there are a few more cool pieces that should be given special attention.

Async support


I'll start with my beloved - native support for asynchronous values ​​based on promises. It looks like this:

 {{#await promise}} <p>loading...</p> {{then data}} <p>got data: {{data.value}}</p> {{catch err}} <p>whoops! {{err.message}}</p> {{/await}} 

This is fucking easy life. In Ractive, we can also do such things, though for this you have to use a special Promise-adapter . Svelte gives it out of the box.

Namespaces


A thing that I haven’t seen anywhere else - using the namespaces you can determine the scope of the component. For example, if our component is applicable only for use inside an svg tag, we can specify this:

 <g transform='translate({{x}},{{y}}) scale({{size / 366.5}})'> <circle r="336.5" fill="{{fill}}"/> <path d="m-41.5 298.5c-121-21-194-115-212-233v-8l-25-1-1-18h481c6 13 10 27 13 41 13 94-38 146-114 193-45 23-93 29-142 26z"/> </g> <script> export default { namespace: 'svg' }; </script> 

An interesting opportunity. It is not yet clear how realistic this is, but there are definitely prospects.

<: Self> tags


Also an unusual feature is the recursive inclusion of the component:

 {{#if countdown > 0}} <p>{{countdown}}</p> <:Self countdown='{{countdown - 1}}'/> {{else}} <p>liftoff!</p> {{/if}} 

Quite an unexpected implementation of countdown counter, isn't it? )))

<: Window> tags


A very dubious feature, but I haven't met anywhere else either. This type of tag allows declarative hanging of events on the window:

 <:Window on:keydown='set({ key: event.key, keyCode: event.keyCode })'/> 

Sapper


I learned about this project just yesterday. The project is nothing more than an outline of ideas, but it seems that Rich is going to do something like an analogue of Next.js / Nuxt.js. I am sure that if everything works out, the analog will be made with Rich’s intrinsic share of individuality.

Epilogue


Thanks to all those who read to the end. As they say, put likes and subscribe to my channel and may the Force be with you!

But seriously, I hope I managed to tell in general about such an interesting phenomenon as Svelte. From myself I can add that the use of Svelte in already two projects fully justified itself. Personally, I lack a bit of the wealth of Ractive capabilities, but minimalism, as well as the very principle of work, Svelte strongly impresses on using it further. Now I’m seriously thinking about using it in a new, rather large project and with time it is possible to completely move from Ractive to Svelte.

I hope you will discover Svelte too. Good luck!

UPDATE:


Thank you serjoga for the addition. StencilJS seems to follow the same path as Svelte, only it has a more complex, “Angular2-style” syntax and JSX.

UPDATE 2:


Who is interested in Svelte and would like to follow its development - welcome to the Russian-language telegram channel SvelteJS . We will be glad to you!

Source: https://habr.com/ru/post/345028/


All Articles