Some performance optimization points in the process of ANGULARJS framework _angularjs

Source: Internet
Author: User
Tags variable scope

1. Introduction

Whether you're writing an old application or using ANGULARJS in a large application, performance is an important aspect. It is important to understand what causes the ANGULARJS application to slow down, and it is important to know that trade-offs are made during the development process. This article will introduce some ANGULARJS more common performance issues, as well as recommendations for optimization.

2. Performance testing Tools

This paper adopts the benchmark of Jsperf http://jsperf.com/performance test.

3. Software performance

There are two basic factors for evaluating software performance:

The first is the time complexity of the algorithm. A simple example is a very significant performance gap between linear and binary search.

The second reason for slow software is called space complexity. This is how much a computer needs "space" or memory to run your application. The more memory requirements, the slower the running speed.


4 Performance of JavaScript

Some performance problems are not just angular, but JavaScript.

4.1 Cycle

Avoid calling functions inside loops, which can be moved to an external call.

var sum = 0;
for (var x = 0; x < x. x + +) {
 var keys = Object.keys (obj);
 sum = sum + keys[x];
}

The above aspect obviously does not have the following fast:

var sum = 0;
var keys = Object.keys (obj);
for (var x = 0; x < x. x + +) {
 sum = sum + keys[x];
}

4.2 Dom Access

Be careful when getting DOM elements

Angular.element (' Div.elementclass ')

This way is very expensive. In fact, this does not cause too much problem in the Angularjs. But it's good to keep an eye out. Dom trees are small and dom accesses as little as possible.

4.3 Variable scope garbage collection

Keep your variables as tightly bound as possible so that the garbage collector can reclaim space more quickly. Note the following questions:

Function Demo () {
 var b = {childfunction:function () {
  console.log (' Hi ' the Child function ')
 };
 B.childfunction ();
 return b;
}


When this function is finally in place, there is no reference to B. B will be recycled. But if you have a line like this:

var cfunc = demo ();

This reference will prevent garbage collection. Try to avoid such references.

4.4 Arrays and objects

Here are a lot of points:

Like what:

for (var x=0; x<arr.length; x + +) {
 i = Arr[x].index;
}

Faster than this one (note * arr is an array, obj is a JSON object)

for (var x=0; x<100; x + +) {
 i = Obj[x].index;
}

A little faster than this one.

var keys = Object.keys (obj);
for (var x = 0; x < Keys.length + +) {
 i = Obj[keys[x]].index;
}


5 Important Concepts

We've talked about the performance of JavaScript, and now it's important to look at the core concepts in ANGUALRJS and see how it works.

5.1 Domain (scopes) and update cycle (Digest Cycle)

The angular domain is essentially a JavaScript object that inherits from some predefined objects. Basically, a small domain runs faster than a large field.

In other words, every time you create a new domain, you add more content to be recycled to the garbage collector.

A core concept and performance impact aspect that is particularly important in writing ANGULARJS applications is the update cycle (Digest Cycle). In fact, each field holds an array of methods consisting of $ $watchers.

Whenever a value (property) or bound DOM, such as Ng-repeat,ng-switch and ng-if, and so on in a domain is invoked, a function (functions) is added to the $ $watchers array queue in the corresponding field.

When the value in the domain changes, all watchers functions in the $ $watchers are invoked. And when any one of them modifies a value in the domain, they are triggered again by the execution.

This process continues until the $ $watcher array queue no longer makes any changes or throws an exception.

More outside if any code executes $scope. $apply (), the update cycle is triggered.

The last point is $scope. Evalasync () executes in an asynchronous call and does not call its update cycle during the current and next execution cycles.


6. General guidelines to be followed in designing angular

6.1 Large objects and server calls

So what did all this tell us? First, we need to simplify our objects as much as possible. This is especially important when the object is returned from the server.

Converting a row in a database directly to an object is only a temporary solution, so do not use the. Tojson ().

You just need to return the property values that angular needs.

6.2 Monitoring functions (watching functions)

Another common problem is the functions that are bound to the observer. Do not bind anything (ng-show, ng-repeat, etc.) directly to a function. Do not directly monitor the return value of any function. This function is executed in each update cycle, and may slow down your application.

6.3 Monitoring objects (watching Objects)

Similarly, angular provides a third optional parameter to monitor changes to an entire object. Set the third argument of the calling $watch to True. This is a very scary idea. A better solution is to rely on service and object references to monitor changes between domains.

7 List Issues

7.1 long list (Lists)

Do some possible to avoid long lists. Ng-repeat will perform some very heavy DOM operations (not to mention the $ $watchers pollution), so try to use small data for rendering, either on pagination or in infinite scrolling.

7.2 Filter (Filters)

Try to avoid using filters. They will run two times in each update cycle, one at a time whenever any change occurs, and another to be triggered when a deeper change is collected. So don't remove objects directly from the internal list, use CSS control. (Note * To hide them with the Add CSS class name)

When rendered $index value is not a true array index value, it is of no value. But a sorted array index doesn't allow you to traverse all the fields in the list.

7.3 Update Ng-repeat

When using ng-repeat, try to avoid refreshing the global list. Ng-repeat produces a $ $hashkey attribute and a system-only item. This means that when you call Scope.listboundtongrepeat = Serverfetch (), a refresh of the entire list is caused. Notifies you that all watchers are executed and triggers each element, which is very consuming performance.

Here are a couple of solutions. One is to maintain two sets, and ng-repeat with filters (which basically require custom synchronization logic, so the algorithm is more complex and less maintainable), and the other is to use track by to specify your own key (angular 1.2 to start support, Requires little synchronization logic only).

Anyway:

Scope.arr = Mockserverfetch ();

It's going to be slower than the following.

var a = Mockserverfetch ();
for (var i = scope.arr.length-1 I >=0; i--) {
 var # = _.find (A, function (r) {return
 (R && r.track Ingkey = = Scope.arr[i].trackingkey);
 };
 if (!result) {
 Scope.arr.splice (i, 1);
 } else {
 a.splice (A.indexof (Scope.arr[i)), 1);
 }
_.map (A, function (newitem) {
 scope.arr.push (newitem);
});


This

<div ng-repeat= "A in arr track by A.trackingkey" >


It's a little slower than the top.

<div ng-repeat= "A in arr" >

8 Rendering problems

Another reason for the slow application of angular is to use ng-hide/ng-show or Ng-switch incorrectly.

Ng-hide and Ng-show simply switch the CSS display properties. This means that the seemingly invisible thing still exists in the domain, and all the $ $watchers will still be triggered.

Ng-if and Ng-switch are actually completely removed from the DOM and the corresponding fields are removed. Performance differences are obvious.

9. Update cycle issues

9.1 Binding

Try to reduce your bindings. In angular 1.3, here is a new binding syntax, {{:: Scopevalue}}. It is only executed once by the domain and is not added to the monitor to monitor the list (watcher array).

9.2 $digest () and $apply ()

Scope. $apply is a powerful tool that allows you to introduce external values to angular. In essence, it triggers all angular events (such as Ng-click). The problem is scope. $apply starts at the root domain $rootscope, loops through all the domain chains, triggering each domain.

Scope. $digest only executes the specified domain and its associated fields. The two performance differences are self-evident. The compromise scenario is to not trigger any domains until the next update cycle is updated.

9.3 $watch ()

Scope $watch () has been discussed in a number of scenarios. Basically scope. $watch is a sign of bad design. If you have to create an observer. Remember to try to decouple it as much as possible. You can use the $watch return function to bind.

var unbinder = scope. $watch (' Scopevaluetobewatcher ', function (newval, oldval) {
 
});
Unbinder (); This row will be watcher from the $ $watchers.

If you cannot bind earlier, remember to bind in the $on (' $destroy ').

9.4 $on, $broadcast and $emit

Like $watch, they are slow events, and (possibly) traversing the entire scope. They may be like Goto, so your program can not debug. But luckily, like $watch, they can all be tied up in a Shihou that is not needed at all. For example, in $on (' $destroy ').

9.5 $destroy

As mentioned earlier, you should $on all of your event listeners in the ' $destroy ', canceling any $timeout instances, or any other asynchronous execution of the interaction. This is not just about ensuring security. It also allows your domain to be garbage collected faster. Don't do it, they'll always run in the background. Directly you empty the CPU and RAM.

In addition, it is also important to bind event listeners on the DOM, and not doing so is likely to cause memory leaks in older browsers.

9.6 $evalAsync

Scope. $evalAsync is a powerful tool. It can be executed in the current domain and does not trigger updates to the domain. Evalasync can greatly improve the performance of your Web pages.

10 Command Questions

10.1 Isolated domains (isolate scope) and transclusion

Domain isolation and transclusion are the most exciting features of angular, and they are the core components of angular.

But there are also trade-offs that directives cannot directly create a domain that replaces their parent group elements. By isolating the domain or transclusion we can create a new object to track, add a new monitor, but this will also degrade the performance of the application. Before adding, you should think carefully if there is any need.


10.2 Compilation Cycle

The compile function of the Directive (Directive) is the perfect function of manipulating the DOM before the domain is attached (for example, binding events). An important aspect of performance is that the elements and attributes passed into the compile function are rendered in the original HTML template. will only be run once, and will be used directly next. Another important point is the difference between PreLink and Postlink. The prelink is executed from the outward. Postlinks is executed from within. PreLink performance is slightly better because it does not produce a second update cycle. However, the DOM of the child element has not yet been created.


One DOM event problem

Angular provides a number of predefined DOM event directives. Ng-click,ng-mouseenter,ng-mouseleave and so on. These events are executed when the Scole. $apply () is invoked. Another more efficient way is to bind AddEventListener directly on the DOM and use scope as much as possible. $digest

Optimizing instances


Testing an application framework is really a serious challenge, when users click on any word in the log, we need to search for relevant information, and the page can be clicked on countless elements, we want to make the log paging function also instantaneous feedback. We have in fact already obtained the next page of the log data, so the user interface update becomes a bottleneck, if the ANGULARJS directly to implement the log view of the page-changing function requires 1.2 seconds, but if carefully optimized then it can be reduced to 35 milliseconds. These optimizations are proven to be applicable in other parts of the application as well as for angularjs adaptability. But we have to break some rules to realize our ideas and discuss them later.

A GitHub updated log demo

An ANGULARJS log viewer

In essence, the log view is a list of log messages, and each word can be clicked. So add the angular instructions to the DOM element and simply implement the following:

<span class= ' logline ' ng-repeat= ' line in Loglinestoshow ' > <span class= ' logtoken ' ' ng-repeat= ' token '
 Line ' >{{token | formattoken}} </span>
 
</span>

It is normal to have thousands of tokens in a single page application, and in earlier tests we found that the next page into the log would take several seconds to execute JavaScript. To make things worse, unrelated operations (such as clicking on a navigation dropdown) are too late, Angularjs's great God says it's best to keep the number of data elements bound below 200. For a word is an element of us, already far beyond this number.

Analysis:

With the Chrome JavaScript Profiler tool, we can quickly locate two stall points. First, each update takes a lot of time to create and destroy DOM elements, and if the new view has a different number of rows, or if any row has different numbers of words, the angular ng-repeat instruction creates or destroys the DOM element, which is too expensive.

Second, each word has its own change Watcher,angularjs will watch these words, once the mouse clicks will trigger, this is the impact of unrelated operations (Drop-down menu navigation) The culprit of the delay.

Optimize #: Cache DOM Elements

We created a variant of the Ng-repeat directive, and in our version, if the number of bound data is reduced, the DOM elements that are exceeded are hidden rather than destroyed, and we reuse the cached elements if the number of elements increases later.

Optimize #2:aggregate watchers

Most of the time used to invoke the change watchers is wasted, and in our application, the data binding on a particular word will never change unless the entire log message changes, in order to achieve this, we create an instruction "hides" to hide the changing of the child element. Watchers, they are invoked only when certain parent element expressions are modified. In this way, we avoided the overall change watchers in each mouse click or other minor modification (in order to implement this idea, we have slightly modified the Angularjs abstraction layer, we will detail later).

Optimizing #3: Delaying element creation

As we said, we created the DOM for every word in the log, we can use a single DOM element of each row to get the same visual rendering; Other elements are created in response to the mouse-point operation, so we decided to postpone this part of the creation, and we'll create it only when the mouse moves to a certain line.

To do this, we created two versions for each row, one simple text element to display the full log information, and the other a placeholder to display the effect of the final fill for each word. This placeholder is initially hidden and is displayed when the mouse is moved to that line, and the simple text line is hidden at this time. Here's how to show how placeholders fill the word elements.

Optimizing #4: Avoiding monitoring of hidden elements

We created another instruction to block the monitoring of hidden elements, which supports optimized #, and we have more hidden DOM nodes than the original data, so we have to eliminate the need to monitor the DOM nodes that are out of the picture. This also supports optimizing #3, making it easier to postpone the creation of word nodes. Because we will not create him until the tokenized version of this line of data appears.

The following code is all optimized, and our custom instructions are bold.

<span class= ' logline ' sly-repeat= ' line in Loglinestoshow ' sly-evaluate-only-when= ' loglines ' >
 <div Ng-mouseenter= "mousehasentered = true" >
  <span ng-show= '!mousehasentered ' >{{logline | formatline}} </ span>
  <div ng-show= ' mousehasentered ' sly-prevent-evaluation-when-hidden> <span
   ' Logtoken ' sly-repeat= ' tokens in line ' >{{token | formattoken}}</span>
  </div>
 </div>
 
</span>

Sly-repeat is a variant of ng-repeat, used to hide more dom elements than to destroy them, sly-evaluate-only-when prevent internal change watchers unless the "loglines" variable is modified, Sly-prevent-evaluation-when-hidden is primarily responsible for the hidden div to display when the mouse is moved above the specified line.

This shows Angularjs's control over encapsulation and separation, we did a complex optimization but did not affect the structure of the template (the code shown here is not the code in the real product, but he shows all the points).

Results:

Let's take a look at the effect, we added some code to measure, starting with the mouse click, until the end of the angular ' s $digest loop (which means updating the DOM end).

We measure the performance of clicking the "Next" button via Tomcat log, the environment is based on the MacBook Pro Chrome, the results are shown in the following table (each data is the average of 10 Tests):

Data has been cached Getting data from the server
Simple implementation 1190 MS 1300 MS
After optimization Ms 201 ms

This data does not include the time that the browser used for the DOM layout and redraw (after JavaScript execution was completed), at approximately 30 milliseconds each time. Still, the effect is obvious; the response time of the next page dips from 1200 milliseconds to 35 milliseconds (if the rendering is 65 milliseconds).

The data in "Get data from the server" includes the time we use Ajax to get log data from the backend. This is different from clicking on the next page button, because we'll take a page of log data, but it might be suitable for other UI responses. Even so, the optimized program can be updated in real time.

Related Article

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.