
Good day to all!
This article begins a cycle of publications devoted to basis.js - a framework for creating full-fledged Single Page Application.
About frameworks
The core of modern frontend frameworks is practically not focused on working with data since it does not have structures and algorithms for processing collections.
Instead, the developer is given the opportunity to work independently with the data and to iterate the collections directly in the template, or to operate with DOM elements directly from the controller / component code, again, independently linking the data with their visual presentation.
The disadvantages of such approaches should be obvious.
When manipulating DOM elements from the controller / component:
- it is necessary to independently implement data structures and data set processing algorithms
- increases the complexity and maintainability of the code, as you have to manually work with DOM – elements and establish links between data and DOM – elements
- most often it is impossible to use template engines
When using a template engine in working with a dataset:
- the readability of the template is deteriorating, since the logic is partially transferred to the template
- Does the template engine effectively implement the mechanism for updating parts of views and is it implemented?
- there are still no data set processing mechanisms
')
Previously, all this did not really matter, as the backend was responsible for data processing and their visual presentation.
All that had to be done was to load the page at the right address and the presentation was already formed.
The disadvantage of this approach is the lack of interactivity and dynamism in terms of updating data.
Modern SPA should be autonomous in terms of functionality and contact the server only when it is necessary to synchronize data (save or receive new ones).
Accordingly, all the work on the processing and visualization of this data should be undertaken by the front end.
With the advent of AJAX and WebSocket, it has become much easier to follow data updates and ensure the interactivity of the application.
But AJAX and WebSocket are about working with the network and they do not solve the basic problem - working with data.
Let's say you make a one-page application - a client for VKontakte.
Here are some of the requirements for the application: download, update, search, group, sort friends from the social network.
Then the “music” section is added. Here you need to work with playlists (both your own and friends). Same search.
A “messages” section is added. There is work with "rooms" and messages.
I think the meaning is clear ...
So: music, friends, messages and so on - this is all the data that needs to be sorted, grouped, searched and, finally, visualized. In this case, the visual presentation should be updated in a timely manner in real time.
Thus, in the developer’s arsenal there should be powerful and convenient tools for working, theoretically, with any amount of data.
About template engines
In order not to breed a regular holivar, I will not give as an example concrete specific frameworks. Instead, imagine that there is some abstract framework with a flexible and convenient template engine.
How would we solve the problems of the above SPA?
It's very simple - we download data from the social network server and feed it to the framework template.
Well, the problem of output, we would see, solved.
But here we understand that new elements have been added to the already drawn collection.
What to do?
Again, everything is very simple - we ask the template engine to redraw the view, but with new data.
And everything seems to be good, as long as there is not much data.
Now imagine that we have a collection of 100 objects that we received from the server.
We gave this data to the template engine, it obediently rendered it, but after some time, the server informs us that another item was added to the collection.
What are we doing? Once again, we give the data to the template engine, and he, the attention, redraws all the previously drawn data.
Not very effective, is it? Especially if there are more items in the collection.
I’ll note that here I’m talking about string templating engines that receive a template and data as input, and output a line with HTML – code, which you need to insert into the DOM.
But they are string strings and that they have no idea about HTML and DOM. They do not care what data to insert into which template and what the developer will then do with this data.
Pro smart template engines
In contrast to the usual, string, template engines are smarter.
Their advantage is that they are already “in the know,” that they work with HTML and do not output the string with the HTML code, but the DOM – tree with the data inserted into it. At the same time, each data element that entered the input of such a template engine becomes associated with its corresponding DOM-node. Thus, when changing any element of the collection, only the node with which this element is associated is updated. We return to the example with a list of 100 elements. In the case of a smart template engine, it will determine itself - the contents of which elements of the collection have been modified and will update only the corresponding nodes, without redrawing the entire view.
This approach is undoubtedly more efficient, especially with large data sets.
But, again, even he does not solve the main problem - working with data.
Yes, the ability to use pipe – filters, which allow modifying data before outputting, is built into such template engines, but, firstly: such an approach impairs the readability of the template; secondly: it is the ability of the template engine, and not the framework, which uses the template engine and in more complex situations will not save from code clutter, both from the template and from the controller / component.
As a result, a fundamental problem arises, which has already been mentioned here more than once - data processing.
About basis.js
Basis.js is a framework whose core was built with the expectation of working with data.
A distinctive feature of the framework is the application building model itself.
The finished application on basis.js is a hierarchy of components between which a stream of data circulates.
At the same time, the image of the data flow best reflects the essence of what is happening, because the creation of an application on basis.js is the establishment of links between parts of the application.
Properly built application eliminates the need for even a banal enumeration of DOM – elements.
The framework itself and its flexible tools are responsible for everything.
At the same time, the framework itself is built on abstractions, which allows you to fully use the advantages of polymorphism and, in most cases, abstract away from a specific implementation.
The basics of working with basis.js can be found in
this article written by the author of the framework.
I had the opportunity to continue the cycle of articles.
From theory to practice
One of the main components of any SPA is the application's response to user actions, in other words, timely data update.
If we are talking about data, then in basis.js there is a set of abstractions for describing data.
The simplest of these is the
Token class.
Token allows you to describe a scalar value that you can subscribe to change:
let token = new basis.Token();
Token # attach method - adds a subscriber to change the value of the token.
Token method
# detach - removes a previously added subscriber.
Moreover, one token may depend on another:
let token = new basis.Token(); let sqr = token.as((value) => value * value);
Token # as - creates a new token and automatically signs it to changes in the value of the original token.
Changing the value of the original token, it is transferred to the function specified in
as and its result is written to the generated token.
Thus, you can create a chain of tokens, the value of each of which will depend on the value of the previous token:
let token = new basis.Token(); let sqr = token.as((value) => value * value); let twoSqr = sqr.as((value) => value * 2); let fn = (value) => console.log(' :', value); token.attach(fn); token.set(4);
A token can be destroyed by calling the
Token # destroy method.
A destroyed token becomes absolutely useless, because it stops notifying subscribers about updating its value, and new subscribers can no longer be added to it.
Here is such a simple and convenient mechanism for updating data laid in basis.js.
Let's see how tokens are used in the basis.js components:
let Node = require('basis.ui').Node; let nameToken = new basis.Token(''); new Node({ container: document.body,
And here is the template:
<div> <input type="text" event-input="input"> <div> {name}</div> </div>
Now, when entering data in a text field, the current value will be substituted for
{name} .
In other words: the
Node property
# binding is an object whose properties can be tokens.
The Node subscribes to changes in the value of such tokens and updates the view in a timely manner, with only those parts that have really changed.
Of course, I can't ignore the example with
Token # as :
let Node = require('basis.ui').Node; let nameToken = new basis.Token(''); new Node({ container: document.body, template: resource('./template.tmpl'), binding: { name: nameToken.as(value => value.toUpperCase()) }, action: { input: (e) => nameToken.set(e.sender.value) } });
Already guessed what will be displayed?
Of course you can argue, they say:
pppffff ... in the anguly the same thing is done without a single line of code
Yes, but later you will see how elegantly basis.js copes with much more complex tasks.
Despite the fact that in this part of our cycle there was more theory than practice, we considered one of the most important aspects of basis.js, which will help us in understanding further topics.
If you, like me, love ES6 and want to use it together with basis.js, then you will need
this plugin .
Thanks for attention!
Many thanks to
lahmatiy for invaluable advice;)
Some useful links:
UPD:The second part ofWe launched
gitter chat on basis.js. Add, ask questions.