Record a failed jQuery optimization Attempt

Source: Internet
Author: User

I often complain that jQuery's DOM operation performance is not good and I often try to optimize it using some methods. But the more optimized, the more frustrated I find that jQuery is actually doing well, optimization from the user's point of view is really Limited (this does not mean that jQuery's performance is excellent, on the contrary, it can only be said that it is a relatively closed library and cannot be optimized by external intervention ). This article records a failed optimization experience.

Optimization ideas

The idea of this optimization comes from the database. During database optimization, we often say that putting a large number of operations in one transaction can effectively improve the efficiency. Although I don't know the reason why I don't know about the database, the idea of transactions has pointed out the direction for me (although it is wrong ).

Therefore, I tried to introduce the transaction concept to jQuery. By opening and committing transactions, I optimized jQuery externally. The most important thing is to reduce the number of cycles of the each function.

As we all know, jQuery's DOM operations are based on get all and set first. Operations used to set DOM attributes/styles are almost all traversal of Selected elements. the access function is the core part. The code used for loop is as follows:

 
 
  1. // Setting one attribute  
  2. if ( value !== undefined ) {  
  3.     // Optionally, function values get executed if exec is true  
  4.     exec = !pass  exec  jQuery.isFunction(value);  
  5.  
  6. for ( var i = 0; i  length; i++ ) {  
  7. fn(  
  8. elems[i],  
  9. key,  
  10. exec ? value.call(elems[i], i, fn(elems[i], key)) : value,  
  11. pass  
  12. );  
  13. }  
  14. return elems; 

For example, the jquery.fn.css function is like this:

 
 
  1. jQuery.fn.css = function( name, value ) {  
  2.     // Setting 'undefined' is a no-op  
  3.     if ( arguments.length === 2  value === undefined ) {  
  4.         return this;  
  5.     }  
  6.  
  7. return jQuery.access( this, name, value, true, function( elem, name, value ) {  
  8. return value !== undefined ?  
  9. jQuery.style( elem, name, value ) :  
  10. jQuery.css( elem, name );  
  11. });  
  12. }; 

Therefore, the following code assumes that there are 5000 selected div elements, and 10000 nodes need to be accessed cyclically:

 
 
  1. jQuery('div').css('height', 300).css('width', 200); 

In my mind, in a transaction, it can be like a database operation. By saving all the operations, the transaction is committed in a unified manner and 10000 node accesses are made, reduced to 5000 times, equivalent to doubled performance.

Simple implementation

In a transactional jQuery operation, two functions are provided:

Begin: starts a transaction and returns the object of the transaction. This object has all functions of jQuery, but calling the function does not take effect immediately. It takes effect only after the transaction is committed.

Commit: commit a transaction to ensure that all previously called functions take effect and return the original jQuery object.

It is easy to implement:

Create a transaction object and copy all functions on jQuery. fn to this object.

When calling a function, add the called function name and related parameters to the prepared queue.

When a transaction is committed, the selected elements are traversed once to apply all functions in the queue to each node in the traversal.

The simple code is as follows:

 
 
  1. VarSlice = Array. prototype. slice;
  2. JQuery. fn. begin =Function(){
  3. VarProxy = {
  4. _ Core: c,
  5. _ Queue: []
  6. },
  7. Key,
  8. Func;
  9. // Copy the function on jQuery. fn 
  10. For(KeyInJQuery. fn ){
  11. Func = jQuery. fn [key];
  12. If(TypeofFunc ='Function'){
  13. // The key generated by the for loop is always the value of the last loop. 
  14. // Therefore, a closure must be used to ensure the validity of the key) 
  15. (Function(Key ){
  16. Proxy [key] =Function(){
  17. // Put the function call into the queue 
  18. This. _ Queue. push ([key, slice. call (arguments, 0)]);
  19. Return This;
  20. };
  21. }) (Key );
  22. }
  23. }
  24. // Prevent the commit function from being blocked. 
  25. Proxy. commit = jQuery. fn. commit;
  26. ReturnProxy;
  27. };
  28.  
  29. JQuery. fn. commit =Function(){
  30. VarCore =This. _ Core,
  31. Queue =This. _ Queue;
  32. // Only one each loop 
  33. Core. each (Function(){
  34. VarI = 0,
  35. Item,
  36. Jq = jQuery (This);
  37. // Call all functions 
  38. For(; Item = queue [I]; I ++ ){
  39. Jq [item [0]. apply (jq, item [1]);
  40. }
  41. });
  42. Return This. C;
  43. };

Test Environment

The test uses the following conditions:

Put the 5000 divs in a container (div id = "container"/div.

Use $ (# containerdiv) to select the 5000 Divs.

Each div requires a random background color (randomColor function) and a random width below 800px (randomWidth function ).

Three call methods are available for testing:

Normal usage:

 
 
  1. $('#containerdiv')  
  2.     .css('background-color', randomColor)  
  3.     .css('width', randomWidth); 

Single Cycle method:

 
 
  1. $('#containerdiv').each(function() {  
  2.     $(this).css('background-color', randomColor).css('width', randomWidth);  
  3. }); 

Transaction method:

 
 
  1. $('#containerdiv')  
  2.     .begin()  
  3.         .css('background-color', randomColor)  
  4.         .css('width', randomWidth)  
  5.     .commit(); 

Object Assignment Method:

 
 
  1. $('#containerdiv').css({  
  2.     'background-color': randomColor,  
  3.     'width': randomWidth  
  4. }); 

Select the Chrome 8 series as the test browser (the Chrome 8 series will be suspended after IE testing ).

Sad results

The original prediction result is that the efficiency of the single cycle method is much higher than that of the normal use method. At the same time, although the transaction method is slower than the single cycle method, It should be faster than the normal use method, the object Assignment Method is actually a single cycle method supported by jQuery, and the efficiency should be the highest.

Unfortunately, the results are as follows:

Normal use Single Cycle Method Transaction method Object Assignment Method
18435 ms 18233 ms 18918 ms 17748 ms

As a result, the transaction method is the slowest method. At the same time, a single loop has no obvious advantages over normal use, and even the object Assignment Method Based on jQuery's internal implementation has not opened a big gap.

Since the operation of 5000 elements is already a very large cycle, such a large cycle has not opened the performance gap, at ordinary times, the most commonly used element operations of about 10 may not have obvious advantages, or even expand the disadvantages.

The reason is that the single-cycle method does not significantly improve the performance. Therefore, it relies on a single loop and is a transaction method built on a single loop, naturally, on the basis of a single loop, additional overhead such as creating transaction objects, saving function queues, and traversing function queues are also required. It is also reasonable to lose the result to the normal use method.

At this point, we can announce the failure of imitating the optimization of the transaction. However, this result can be further analyzed.

Performance

First, analyze the code usage and compare the normal use method with the fastest object Assignment Method in the test, it can be said that the difference between the two lies only in the difference in the number of elements in the loop (aside from the internal problems of jQuery, in fact jQuery. the poor implementation of access does drag the object assignment method, but it is not serious). The normal use method is 10000 elements, and the object Assignment Method is 5000 elements. Therefore, we can simply think that 18435 17748 = 5000 MS is the time-consuming cycle of 3.5% elements, which accounts for about of the entire execution process and is not the backbone of the entire execution process, in fact, there is really no need for optimization.

So where does the additional 96.5% overhead go? Remember the Doglas sentence. In fact, Javascript is not slow, but DOM operations are slow. In fact, the remaining 96.5% overhead, except for the basic consumption of function calls, at least 95% of the time is spent on the re-rendering after the DOM element style is changed.

After discovering this fact, there is actually a more correct direction for optimization. It is also one of the basic principles in front-end performance: when modifying large quantum elements, first, remove the root parent DOM node from the DOM tree. Therefore, if the following code is used for testing:

 
 
  1. // No reuse $ ('# iner') is already bad. 
  2. $ ('# Iner'). Detach (). find ('Div')
  3. . Css ('Background-color', RandomColor)
  4. . Css ('Width', RandomWidth );
  5. $ ('# Iner'). AppendTo (document. body );

The test results remain around MS, which is no more than an order of magnitude than the previous data. The optimization is successful.

Lessons learned and summary

You must find the correct performance bottleneck before optimization. Blind guesses will only lead to a wrong and extreme path.

No one speaks before the data!

I don't think the Transaction direction is wrong. If jQuery native can support the concept of transactions, will there be other points that can be optimized? For example, a transaction automatically disconnects the parent element from the DOM tree.

Link: http://www.otakustay.com/a-failure-in-jquery-optimization/

  1. JQuery 1.5 official version released 5 major changes eye-catching
  2. 18 latest and most powerful jQuery tutorials
  3. Use jQuery to simplify Ajax Development
  4. Dynamic addition and statistics of table data using jQuery
  5. JQuery1.5 redemption of plug-in mechanism of new features

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.