
basis.js and what tools you can use. As an example, several simple ideas will be created, the issue of modularity and organization of project files will be raised.Google Chrome )basis.js do not require assembly during development. But for their work requires a web server. Any web server may be suitable, but it is better to use the dev-server included in basisjs-tools , as it provides more opportunities for development.basisjs-tools is a set of console tools written in javascript and running under node.js This kit includes a collector, a dev server and a code generator. It is installed as a normal npm module: > npm install -g basisjs-tools -g flag), then the basis command will be available in the console. > basis server 8000 (this can be changed using the --port or -p flag). Now you can open http://localhost:8000 in the browser and make sure that the server is working. Nevertheless, it gives an error, since the folder of our project is still empty. Let's fix this.basis.js to the project. To do this, you can either clone the project from the repository , or use bower . > bower install basis html file of the application, which so far will only basis.js - index.html . <!doctype html> <html> <head> <meta charset="utf-8"> <title>My first app on basis.js</title> </head> <body> <script src="bower_components/basisjs/src/basis.js" basis-config=""></script> </body> </html> basis-config attribute of the <script> .basis.js find the <script> to which it was connected. This is necessary in order to determine the path to the basis.js sources and allow the paths to its modules. <!doctype html> <html> <head> <meta charset="utf-8"> <title>My first app on basis.js</title> </head> <body> <script src="bower_components/basisjs/src/basis.js" basis-config=""></script> <script> basis.require('basis.ui'); var view = new basis.ui.Node({ container: document.body, template: '<h1>Hello world!</h1>' }); </script> </body> </html> basis.ui module, using the basis.require function. This function works in almost the same way as the require function in node.js and can connect modules by their name or file name. In this case, basis.ui is the name of the module. As we will see, this function can “connect” any file by its name.basis.ui module, since it provides everything needed to build the interface. This module requires other modules, but this does not need to be taken care of, leave it basis.js . It is necessary to connect only what is directly used in the code that you write.basis.ui.Node . Do not be confused by the name Node , instead of the traditional View . The fact is that in basis.js all components and views are embedded in each other. So, a block may look like a whole, but in fact it may consist of a set of nested representations (nodes).DOM . But we will come back to this.container property, we specified where to put the DOM view fragment when it is created. This must be a DOM element. And in the template property indicated the description of the template. This description is specified in the config for example. Such an opportunity to specify the description of the template in the config line is convenient for prototyping and examples. But for published (production) applications it is not used and we will redo it later.hello.js and transfer to it what was specified in the <script> .index.html : <!doctype html> <html> <head> <meta charset="utf-8"> <title>My first app on basis.js</title> </head> <body> <script src="bower_components/basisjs/src/basis.js" basis-config=""></script> <script> basis.require('./hello.js'); </script> </body> </html> basis.require function is basis.require , but this time the path to the file is passed to it. It is important that the file path starts with " ./ ", " ../ " or " / ". So basis.require will unambiguously understand that the value is the path to the file, and not the name of the module. The same convention applies to node.jshello.tmpl . Then the presentation code will look like this: basis.require('basis.ui'); var view = new basis.ui.Node({ container: document.body, template: basis.resource('./hello.tmpl') }); basis.resource . This function creates an "interface" to the file. This approach makes it possible to determine which files are needed, without downloading them until there is no need for it.basis.resource is a function with additional methods. Calling such a function, or its fetch method, causes the file to be loaded. The file is loaded only once, and the result is cached. More details can be found in Resources (modularity) .basis.require('./file.name') equivalent to basis.resource('./file.name').fetch() .basis.require . But templates are often described in classes, and for these cases it is not necessary to load the file until the first instance of the class is created. We will see this in other examples. Therefore, for consistency: when assigning a template, it is better to always use basis.resource .__filename , and the folder where the module is located from the variable __dirname .require and resource become available. They work in the same way as basis.require and basis.resource , except for how relative file paths are resolved. If a relative path is passed for the functions basis.require and basis.resource , then it resolves the relative html file (in our case, this is index.html ). At the same time, require and resource allow such paths relative to the module (that is, its __dirname ).require and resource . Thus, the hello.js code hello.js bit simpler: require('basis.ui'); var view = new basis.ui.Node({ container: document.body, template: resource('./hello.tmpl') }); javascript modules, but also to other types of content. So, for example, if the template description is in a separate file, then when it is changed, it is not necessary to refresh the page. As soon as changes are saved, all instances of views that use the modified template independently update their DOM fragments. And all this happens without reloading the page, while maintaining the current state of the application.css , localization files and other file types. The only changes that require reloading the page are changing the html file and changing the javascript modules that have already been initialized.basisjs-tools dev server. This is one of the main reasons why it is worth using it, and not a regular web server.hello.css , such as: h1 { color: red; } hello.tmpl template a bit ( hello.tmpl ): <b:style src="./hello.css"/> <h1>Hello world!</h1> <b:style> tag to the template. This tag says that when this template is used, the specified style file must be connected to the page. Relative paths are resolved relative to the template file. One template can include an arbitrary number of style files. We do not need to worry about adding and deleting styles, the framework takes care of this.DOM fragment. Unlike most template engines, basis.js templates basis.js not have direct access to presentation properties. And they can use only those values ​​that the representation itself provides to the template.binding property in the description of the instance or class inherited from basis.ui.Node . Values ​​are specified as an object, where the key is the name that will be available in the template, and the value is the function that calculates the value for the template. To such functions, the only parameter is the template owner, that is, the representation itself. This is how you can provide the name value to the template: require('basis.ui'); var view = new basis.ui.Node({ container: document.body, name: 'world', template: resource('./hello.tmpl'), binding: { name: function(node){ return node.name; } } }); binding property is an auto-extensible property . When a new value is set for a property, when an instance or class is created, the new value extends the previous one by adding and overriding the previous values. By default, basis.ui.Node already has several useful values that can be used along with the name we defined.hello.tmpl ) to use the name . <b:style src="./hello.css"/> <h1>Hello, {name}!</h1> {name} , to insert the value as plain text.basis.js template basis.js works with DOM nodes. For this description will be created the element <h1> , which will contain the three text nodes " Hello, ", " {name} " and " ! ". The first and last will be static and their text will not change. But the average will be assigned the value from the view (its nodeValue property will change). <b:style src="./hello.css"/> <div> <h1>Hello, {name}!</h1> <input value="{name}" event-keyup="setName"/> </div> <input> element has been added to the template. For its value attribute, the same binding is used as in the header - {name} . But this only works for writing to the DOM .DOM fragment, an attribute is added to the desired element, the name of which is the name of the event with the prefix " event- ". We can add the execution of an action to any element on any event. And there can be several actions for one event, the main thing is to separate the names of actions by a space.event-keyup , which requires the view to perform a setName action when the keyup event keyup . If the view does not have any action defined, then we will see a warning message on the console and nothing more will happen.action property. It works like binding , but only describes actions. Functions in action receive a parameter event object. This is not an original event, but a copy of it with additional methods and properties (the original event is stored in its event_ property).hello.js ): require('basis.ui'); var view = new basis.ui.Node({ container: document.body, name: 'world', template: resource('./hello.tmpl'), binding: { name: function(node){ return node.name; } }, action: { setName: function(event){ this.name = event.sender.value; this.updateBind('name'); } } }); event.sender , and this is the element at which the event occurred - <input> . In order for the view to re-evaluate the value and pass it to the template, we called the updateBind method.data property and changed by the update method. When values ​​in data change, the update event is fired. We use this mechanism to store the name: require('basis.ui'); var view = new basis.ui.Node({ container: document.body, data: { name: 'world' }, template: resource('./hello.tmpl'), binding: { name: { events: 'update', getter: function(node){ return node.data.name; } } }, action: { setName: function(event){ this.update({ name: event.sender.value }); } } }); updateBind not called explicitly. But to describe the bindig it took more code. Fortunately, binders have helpers that reduce the description of common situations. Synchronization with the field of data one of them. Such a binding can be written in a shorter form, like this: require('basis.ui'); var view = new basis.ui.Node({ container: document.body, data: { name: 'world' }, template: resource('./hello.tmpl'), binding: { name: 'data:name' }, action: { setName: function(event){ this.update({ name: event.sender.value }); } } }); binding for this. And the template intercepts and transfers to representation of an event, causing actions from action . In fact, binding and action main points of contact between the view and the template. At the same time, the representation knows practically nothing about the device of the template, and the template knows about the presentation device. All logic ( javascript ) is on the side of the view, and work with the display ( DOM ) is on the side of the template. So, in most cases, a complete separation of logic and representation is achieved.
list.js file with the following content: require('basis.ui'); var list = new basis.ui.Node({ container: document.body, template: resource('./list.tmpl') }); var Item = basis.ui.Node.subclass({ template: resource('./item.tmpl'), binding: { name: function(node){ return node.name; } } }); list.appendChild(new Item({ name: 'foo' })); list.appendChild(new Item({ name: 'bar' })); list.appendChild(new Item({ name: 'baz' })); hello.js , but new constructions have been added.basis.js them, we note that the component.js approach is used in basis.js . So, if we make, for example, a list, then it will be not one presentation, but several. One view is the list itself. And each element of the list is also a representation. So we separately describe the behavior of the list, and the behavior of the elements of the list. A little more detail about this approach, for example, is described in the report “Component Approach: Boring, Uninteresting, Unpromising”: slides and video .childNodes property), and for them, the view in which they are nested is the parent (the link is stored in the parentNode property).basis.ui.Node . This class contains the template file and simple binding. After that, three instances of this class were created and added to the list.DOM principles are used to organize the tree of representations. Use the appendChild and insertBefore methods for insertion, removeChild to remove, and replaceChild to replace. There are also non-standard methods: setChildNodes allows setChildNodes to set a list of child views, and clear deletes all child views in one go. require('basis.ui'); var list = new basis.ui.Node({ container: document.body, template: resource('./list.tmpl') }); var Item = basis.ui.Node.subclass({ template: resource('./item.tmpl'), binding: { name: function(node){ return node.name; } } }); list.setChildNodes([ new Item({ name: 'foo' }), new Item({ name: 'bar' }), new Item({ name: 'baz' }) ]); require('basis.ui'); var Item = basis.ui.Node.subclass({ template: resource('./item.tmpl'), binding: { name: function(node){ return node.name; } } }); var list = new basis.ui.Node({ container: document.body, template: resource('./list.tmpl'), childNodes: [ new Item({ name: 'foo' }), new Item({ name: 'bar' }), new Item({ name: 'baz' }) ] }); childClassand childFactory. The first defines an instance class that can be added as a child node. And the second property defines the function that is passed, added as a child node, a value that is not an instance childClass. The task of such a function is to create a suitable instance. By default, this function creates an instance childClassusing the transferred value as a config: basis.ui.Node.prototype.childFactory = function(value){ return new this.childClass(value); }; childClass. Then it will be possible to add new items to the list not only by creating an instance Item, but also by passing the config. require('basis.ui'); var Item = basis.ui.Node.subclass({ template: resource('./item.tmpl'), binding: { name: function(node){ return node.name; } } }); var list = new basis.ui.Node({ container: document.body, template: resource('./list.tmpl'), childClass: Item, childNodes: [ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ] }); Itemis not used anywhere else, so it makes no sense to save it to a variable. This class can be immediately set in the config. But this is not all that we can do. When a new class or instance is created and some of its property is a class, and we want to create a new class based on it, it is not necessary to create a class explicitly; you can simply specify an object with extensions of the new class. It sounds difficult, but, in fact, it’s all about the fact that we don’t have to specify basis.ui.Node.subclass, you can just pass the object. And we get: require('basis.ui'); var list = new basis.ui.Node({ container: document.body, template: resource('./list.tmpl'), childClass: { template: resource('./item.tmpl'), binding: { name: function(node){ return node.name; } } }, childNodes: [ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ] }); list.tmpl: <div id="mylist"> <h2> </h2> <ul{childNodesElement}/> </div> ulis an unfamiliar construction {childNodesElement}. Meet this is also a marker. So we say that we want to refer to this element by name childNodesElement. In fact, we personally do not need this link yet. But it is necessary for the list view to understand where to insert DOMfragments of child nodes. If you do not specify it, then the child nodes will be inserted into the root element (in our case it is <div id="mylist">).DOMdirectly, this is what performances do. We only suggest what and where to place. And since representations are involved in moving nodes, they know perfectly well where they are and try to do their job as optimally as possible. Moreover, this is why it is possible to update templates without reloading the page. When the description of the template changes, the presentation creates a new DOMfragment and transfers everything from the old fragment to the new one.item.tmpl): <li> {name} </li> <!doctype html> <html> <head> <meta charset="utf-8"> <title>My first app on basis.js</title> </head> <body> <script src="bower_components/basisjs/src/basis.js" basis-config=""></script> <script> basis.require('./hello.js'); basis.require('./list.js'); </script> </body> </html> DOMfragment. Plug-ins, their views, may also include other child views, and they are their child views, etc. Thus, representations determine which representations are included in them, but not vice versa. Usually, child views do not know who and how includes them.container, since their location will determine the parent view. And secondly, it is necessary that the module returns the view itself so that it can be used. To do this, use exportsor module.exports(all as in node.js).hello.jstake the following form: require('basis.ui'); module.exports = new basis.ui.Node({ data: { name: 'world' }, template: resource('./hello.tmpl'), binding: { name: 'data:name' }, action: { setName: function(event){ this.update({ name: event.sender.value }); } } }); list.js) is: require('basis.ui'); module.exports = new basis.ui.Node({ template: resource('./list.tmpl'), childClass: { template: resource('./item.tmpl'), binding: { name: function(node){ return node.name; } } }, childNodes: [ { name: 'foo' }, { name: 'bar' }, { name: 'baz' } ] }); app.js: require('basis.ui'); new basis.ui.Node({ container: document.body, childNodes: [ require('./hello.js'), require('./list.js') ] }); <div>. While we are satisfied.index.html: <!doctype html> <html> <head> <meta charset="utf-8"> <title>My first app on basis.js</title> </head> <body> <script src="bower_components/basisjs/src/basis.js" basis-config=""></script> <script> basis.require('./app.js'); </script> </body> </html> basis.requirewere replaced by one. But do not write it, but use the option autoloadin basis-config: <!doctype html> <html> <head> <meta charset="utf-8"> <title>My first app on basis.js</title> </head> <body> <script src="bower_components/basisjs/src/basis.js" basis-config="autoload: 'app'"></script> </body> </html> <div>. For this you must use - satellites.satellite. In binders, you can use a helper satellite:to provide a template with the ability to place their DOMfragments within its DOMfragment. At the same time, the root element of the satellite is transmitted to the template (after all, they operate in terms of DOM), and the point to insert is defined in the template itself.app.jswith the use of satellites: require('basis.ui'); new basis.ui.Node({ container: document.body, template: resource('./app.tmpl'), binding: { hello: 'satellite:hello', list: 'satellite:list' }, satellite: { hello: require('./hello.js'), list: require('./list.js') } }); require('basis.ui'); new basis.ui.Node({ container: document.body, template: resource('./app.tmpl'), binding: { hello: require('./hello.js'), list: require('./list.js') } }); basis.ui.Node. In this case, he implicitly becomes a satellite with the name of binding. <div> <div id="sidebar"> <!--{list}--> </div> <div id="content"> <!--{hello}--> </div> </div> 
basis.jsthis is usually 800-1200 files. It is inconvenient and unreasonable to store all files in one folder. Let's try to restructure the file location.helloand transfer files related to this module (ie hello.js, hello.tmpland hello.css). As well as the folder listin which we transfer list.js, list.tmpland item.tmpl. All that is left for us is to change the ways of connecting modules to app.js: require('basis.ui'); new basis.ui.Node({ container: document.body, template: resource('./app.tmpl'), binding: { hello: require('./hello/hello.js'), // list: require('./list/list.js') // } }); 
srcand put there all the files and folders except bower_componentsand index.html. After that, you need to fix one path to index.html: <!-- --> <script src="bower_components/basisjs/src/basis.js" basis-config="autoload: 'app'"></script> <!-- --> <script src="bower_components/basisjs/src/basis.js" basis-config="autoload: 'src/app'"></script> 

module. The main javascriptfile of the module is called index.js. Templates and all that applies to them (styles, images, etc.) are located in a folder template. This is the most frequent project organization structure at the moment.basis.jsare two auxiliary tools: devpaneland a plugin for Google Chrome.devpanel- This is a small panel with buttons that can be dragged. It looks like this:
app.js): /** @cut */ require('basis.devpanel'); /** @cut */, it allows you to cut lines at the assembly. We do not need to show this panel to users, right?Google Web Storehere on this link . For his work is necessary devpanel, as it provides an API to work with basis.js.
basisjs-tools.basisjs-tools.htmlfile as input (in our case index.html). He analyzes it finds the tags <script>, <link rel="stylesheet">, <style>and others. Understands what files are connected. Then proceeds to analyze these files, finds them in the design to connect other files (such as javascriptcalls basis.require, basis.resourceand others, and in the css- @import,url(..)etc.). So, recursively processing all files, the collector builds an application graph. After that, it analyzes the links, rebuilds and optimizes files. And the result of their work is stored in a separate folder, in the form of a much smaller number of files. > basis build index.html, script.jsand style.csslocated in the folder build. These three files are our application in assembled form. All you need to do after assembly is to copy the contents of the folder buildto the server. All the necessary files for the application will be in it.build: > basis build --help javascriptand cssyou can perform by specifying the flag --pack(or its short version -p): > basis build --pack 
--verbose, you can see all of his actions in detail. But we should not take care of it. After all, our main task is not to build, but to create applications and do cool stuff.basis.js, tried various options for creating views and organizing project files. The acquired knowledge will help in the study of the remaining parts of the manual.Source: https://habr.com/ru/post/216577/
All Articles