Practical experience creating Dojo widgets. Javascript code performance optimization
Good day, habrasociety.
I want to share with you some useful, in my opinion, information that will be of interest to those who are developing or planning to start developing their Dojo widgets. I want to immediately warn that in this article I will not talk about how to create my Dojo widget in stages (however, if the community is interested in this topic, I hope to set it out in a separate article). Instead, I want to focus on the main points and principles that should be known and taken into account by everyone who is developing Dojo widgets or has anything to do with this topic.


I think it will not be superfluous to say a few words about the concept of widgets.
The goal of Dojo widgets is to encapsulate visual Web components for reuse later. The Dojo widget is processed on the web browser side.
The structure of Dojo widgets is independent of the structure of DOM objects. Each Dojo widget has a unique widgetId and may not contain any, or contain one or more DOM elements, as well as Dojo child widgets.
A widget consists of several files. A JavaScript file is needed, while others are optional:
Dojo allows us to create widgets in two ways: declaratively and programmatically.
Programmatically, we can instantiate the widget using JavaScript code:
Widgets are declaratively created directly through the markup:
Widgets declared declaratively will be detected and instantiated by the dojo parser immediately after the body.load event and before calling functions from dojo.addOnLoad . Therefore, in order not to block the flow of execution, it is best to defer the execution of resource-intensive tasks on addOnLoad wherever possible.
Dojo developers are clearly inclined to describe the interface using special attributes of HTML tags, and not directly in the JS code. In the early versions of the library, it was the declarative model that was accessed, when in the HTML code the developer described the form using attributes, and after loading in the browser, the Dojo engine replaced the descriptions with rendered elements. Later, after a radical change in architecture and transition to branch 0.9, and then in 1.x, it turned out that the program model is more familiar and, most importantly, faster, especially in complex forms. Yes, and debugging seems simpler, and the possibilities are wider.
The final method for creating a widget is the startup () method. This method is especially useful in cases where we need to perform some actions after the widget has been fully initialized and rendered, but before it is displayed on the page. For example, if we need to add some element to the widget or a new tab to the tab container of the widget, then we can easily do this in the startup method.
The startup method will be automatically called by the parser in the case of the declarative declaration of the widget, however, if we create the widget programmatically, then we need to call this method manually.
As we all know, JavaScript multithreading is out of the question. All JavaScript code will be executed in the same thread. Therefore, the implementation of "heavy" (resource-intensive) tasks is better to put off until the last moment.
To do this, we can use timers in JavaScript, namely the setTimeout and setInterval functions, which allow you to organize delayed code execution after a specified period of time. In this case, the execution of the pending code is shifted in the execution queue of the JS engine, which makes it possible to complete the execution of more critical tasks.
You can read more about using timers to optimize JavaScript code here .
Use dojo.query to access the DOM. Yes, dojo.query is much faster and has a more concise syntax than regular DOM APIs. For example, this code:
using dojo.query looks like this:
You can find more diverse examples of using dojo.query using various queries here .
Use dojo.behavior to add functionality. dojo.behavior is a very simple and easy mechanism for adding code to existing documents, which is based on dojo.query for selecting DOM elements and two simple API commands:
dojo.behavior.add (), which adds a “behavioral object” ( behavior object) to the document, and dojo.behavior.apply (), which applies the added “behavior”.
Use cases can be found here .
"Hanging" the same handlers on several different elements can be optimized using delegation. The meaning of this optimization of event processing is to "hang" one handler on one common parent element and use the event object received in the handler arguments to access the event initiator.
I want to share with you some useful, in my opinion, information that will be of interest to those who are developing or planning to start developing their Dojo widgets. I want to immediately warn that in this article I will not talk about how to create my Dojo widget in stages (however, if the community is interested in this topic, I hope to set it out in a separate article). Instead, I want to focus on the main points and principles that should be known and taken into account by everyone who is developing Dojo widgets or has anything to do with this topic.


Dojo widgets
I think it will not be superfluous to say a few words about the concept of widgets.
The goal of Dojo widgets is to encapsulate visual Web components for reuse later. The Dojo widget is processed on the web browser side.
The structure of Dojo widgets is independent of the structure of DOM objects. Each Dojo widget has a unique widgetId and may not contain any, or contain one or more DOM elements, as well as Dojo child widgets.
A widget consists of several files. A JavaScript file is needed, while others are optional:
- The JavaScript code file (* .js) contains the widget logic in JavaScript. It manages visual elements using the JavaScript DOM Object API. (Dojo has many useful libraries to simplify standard functions.)
- An HTML template (* .html) provides a basic representation of the widget in HTML. If the template is very simple, you can represent it as a string in a code file.
- The CSS style file (* .css) contains the visual styles used by the HTML template and / or JavaScript code.
- Image files (* .gif, * .jpg, etc.) contain the images used by the widget.
Practical experience in creating widgets.
Software and declarative models for creating Dojo widgets
Dojo allows us to create widgets in two ways: declaratively and programmatically.
Programmatically, we can instantiate the widget using JavaScript code:
- var myWidget = new WidgetClass({
- //properties
- },”targetNode”);
* This source code was highlighted with Source Code Highlighter.
Widgets are declaratively created directly through the markup:
-
* This source code was highlighted with Source Code Highlighter.
Widgets declared declaratively will be detected and instantiated by the dojo parser immediately after the body.load event and before calling functions from dojo.addOnLoad . Therefore, in order not to block the flow of execution, it is best to defer the execution of resource-intensive tasks on addOnLoad wherever possible.
- //Медленный вариант:
- slowFunction(){doSlowStuff();}
- //Оптимизированный вариант:
- slowFunction(){dojo.addOnLoad(doSlowStuff());}
* This source code was highlighted with Source Code Highlighter.
Dojo developers are clearly inclined to describe the interface using special attributes of HTML tags, and not directly in the JS code. In the early versions of the library, it was the declarative model that was accessed, when in the HTML code the developer described the form using attributes, and after loading in the browser, the Dojo engine replaced the descriptions with rendered elements. Later, after a radical change in architecture and transition to branch 0.9, and then in 1.x, it turned out that the program model is more familiar and, most importantly, faster, especially in complex forms. Yes, and debugging seems simpler, and the possibilities are wider.
Widget startup
The final method for creating a widget is the startup () method. This method is especially useful in cases where we need to perform some actions after the widget has been fully initialized and rendered, but before it is displayed on the page. For example, if we need to add some element to the widget or a new tab to the tab container of the widget, then we can easily do this in the startup method.
The startup method will be automatically called by the parser in the case of the declarative declaration of the widget, however, if we create the widget programmatically, then we need to call this method manually.
Delayed execution of "heavy" functions.
As we all know, JavaScript multithreading is out of the question. All JavaScript code will be executed in the same thread. Therefore, the implementation of "heavy" (resource-intensive) tasks is better to put off until the last moment.
To do this, we can use timers in JavaScript, namely the setTimeout and setInterval functions, which allow you to organize delayed code execution after a specified period of time. In this case, the execution of the pending code is shifted in the execution queue of the JS engine, which makes it possible to complete the execution of more critical tasks.
- dojo.addOnLoad(function(){
- setTimeout(function(){
- //отложенный код
- }, 100);
- });
* This source code was highlighted with Source Code Highlighter.
You can read more about using timers to optimize JavaScript code here .
dojo.query
Use dojo.query to access the DOM. Yes, dojo.query is much faster and has a more concise syntax than regular DOM APIs. For example, this code:
- // list every node with the class "progressIndicator":
- var list = [];
- var nodes = .getElementsByTagName("*");
- // iterate over every node in the document....SLOOOW
- for(var x = 0; x < nodes.length; x++){
- if(nodes[x].className == "progressIndicator"){
- list.push(nodes[x]); } }
- console.dir(list);
* This source code was highlighted with Source Code Highlighter.
using dojo.query looks like this:
- console.dir( dojo.query(".progressIndicator") );
* This source code was highlighted with Source Code Highlighter.
You can find more diverse examples of using dojo.query using various queries here .
dojo.behavior
Use dojo.behavior to add functionality. dojo.behavior is a very simple and easy mechanism for adding code to existing documents, which is based on dojo.query for selecting DOM elements and two simple API commands:
dojo.behavior.add (), which adds a “behavioral object” ( behavior object) to the document, and dojo.behavior.apply (), which applies the added “behavior”.
Use cases can be found here .
Event delegation
"Hanging" the same handlers on several different elements can be optimized using delegation. The meaning of this optimization of event processing is to "hang" one handler on one common parent element and use the event object received in the handler arguments to access the event initiator.
- 1
- 2
- 3
- 4
- 5
* This source code was highlighted with Source Code Highlighter.
Without delegation:
- for (var i = 1; i < 6; i++) {
- var el = dojo.byId("" + i);
- dojo.connect(el, "onclick", function() {
- alert(this.id);
- }); }
* This source code was highlighted with Source Code Highlighter.
Using delegation:
- dojo.connect(dojo.byId("container"), "onclick", function(evt) {
- var el = evt.target;
- alert(el.id);
- });
* This source code was highlighted with Source Code Highlighter.
On a note
And finally, general ideas for optimizing code using the Dojo framework:
- dojo.parse is faster than directly traversing the DOM;
- adding elements to the DOM is detrimental to performance;
- each widget is an extra load, so combine them where possible;
- organize deferred execution wherever possible;
- Dojo core functions are optimized for performance, however widgets can be slow and resource-intensive if you do not pay the necessary attention to all the details during implementation;
In addition, the following critical factors should be remembered and taken into account:
- Scripts in body slow down body parsing;
- Resource loading blocks body load event;
- Processing massive constructions of widgets to be parsed blocks addOnLoad handlers;
- Massive (slow) addOnLoad handlers block the sequential execution of the remaining handlers.
Conclusion
Well, that’s probably all that I wanted to dwell on in this article. Of course, this article does not claim to be a complete or “best” reference for optimizing JS code and / or Dojo widgets, but I tried to dwell on the most significant and frequently used principles. I hope this article will be useful to everyone who uses or plans to start using the Dojo JavaScript framework .
upd : Thanks for the karma - moved to the Javascript blog.