Sort out some performance optimization points during AngularJS framework usage _ AngularJS

Source: Internet
Author: User
This article mainly introduces some key performance optimization points in the use of AngularJS framework. Angular is usually used to create large single-page applications. Therefore, performance problems must also be considered. For more information, see 1. Introduction

Whether you are writing an old application or using AngularJS in a large application, performance is an important aspect. It is important to know why AngularJS applications are slow. It is important to make a balance during the development process. This article will introduce some common AngularJS performance problems and optimization suggestions.

2. Performance testing tools

This paper uses jsPerf http://jsperf.com/performance testing benchmark.

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 that there is a significant performance gap between linear search and binary search.

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


4. Javascript Performance

Some performance problems come from JavaScript, not just Angular.

4.1 cycles

To avoid calling a function within a loop, you can move it to an external call.

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

The above aspects are obviously not as fast as below:

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

4.2 DOM access

Note the following when getting DOM elements:

angular.element('p.elementClass')

This method is very expensive. In fact, this does not cause too many problems in AngularJS. But pay attention to the benefits. The DOM tree should be small, and DOM access should be as few as possible.

4.3 variable scope of action garbage collection

The closer your variable scope is, the better, so that the garbage collector can recycle space faster. Note the following:

function demo(){ var b = {childFunction: function(){  console.log('hi this is the child function') }; b.childFunction(); return b;}


When this function is complete, there is no reference to B. B will be recycled. However, if there is such a line:

var cFunc = demo();

This reference will prevent garbage collection. Avoid such references whenever possible.

4.4 arrays and objects

There are many points:

For example:

For (var x = 0; x

Faster than this one (note * arr is an array and obj is a json object)

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

Faster than this one

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


5 important concepts

We have discussed the performance of JavaScript. Now it is necessary to take a look at the core concepts in AngualrJS to see how it works.

5.1 domain (Scopes) and update Cycle (Digest Cycle)

Angular fields are essentially some JavaScript objects that inherit from some predefined objects. Basically, Small Domains run faster than large ones.

In other words, each time a new domain is created, more content to be recycled will be added to the garbage collector.

In writing AngularJS applications, a core concept and performance impact should be particularly noted in terms of the update Cycle (Digest Cycle ). In fact, each domain stores an array composed of Methods $ watchers.

Every time a value (attribute) or bound DOM in the field, such as ng-repeat, ng-switch, and ng-if, when $ watch is called, a function) it will be added to the $ watchers array queue in the corresponding domain.

When the value in the field changes, all the watchers functions in $ watchers will be called. And when either of them modifies a value in the field, they will be triggered again.

This process continues until no changes or exceptions are made in the $ watcher array queue.

If any code is executed $ scope. $ apply (), the update cycle is triggered.

The last point is that $ scope. evalAsync () will be executed in an asynchronous call and will not call its update cycle in the current and next execution cycles.


6. general guidelines to be followed when designing Angular

6.1 large object and server calls

So what have these told us? First, we should try to simplify our objects. This is especially important when an object is returned from the server.

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

You only need to return the attribute values required by Angular.

6.2 monitoring Functions (Watching Functions)

Another common problem is the function bound to the observer. Do not bind anything (ng-show, ng-repeat, and so on) to a function. Do not directly monitor the return values of any function. This function will be executed in each update cycle, which may speed down your application.

6.3 Watching Objects)

Angular also provides the third optional parameter to monitor changes to the entire object. Set the third parameter of $ watch to true. This is a terrible idea. A better solution is to monitor changes between domains by referencing services and objects.

7. List Problems

7.1 long list (Lists)

Avoid long list as much as possible. Ng-repeat performs some heavy DOM operations (not to mention the $ watchers pollution), so try to use small data for rendering on pages or in infinite scrolling.

7.2 filter (Filters)

Avoid using filters whenever possible. They run twice in each update cycle and run once when any change occurs. The other is triggered when a deeper change is collected. Therefore, do not directly remove objects from the internal list. Use CSS to control them. (Note * use the CSS class name to hide them)

The $ index value during rendering is not the real array index value, and it has no value. However, sorting array indexes won'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 generates a $ hashkey attribute and a unique item in the system. This means that when you call scope. listBoundToNgRepeat = serverFetch (), it will cause a refresh of the entire list. It will notify the execution of all watchers and trigger each element, which is very performance-consuming.

There are two solutions. One is to maintain two sets, and ng-repeat with a filter (basically requires custom synchronization logic, so the algorithm is more complex and the maintainability is worse ), another solution is to use track by to specify your own key (Angular 1.2 starts to support only a small amount of synchronization logic ).

In short:

scope.arr = mockServerFetch();

It will be slower than the following

var a = mockServerFetch();for(var i = scope.arr.length - 1; i >=0; i--){ var result = _.find(a, function(r){ return (r && r.trackingKey == 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


Slower than above

8 rendering Problems

Another cause of slow Angular applications is the incorrect use of ng-hide/ng-show or ng-switch.

Ng-hide and ng-show simply switch the CSS display attribute. This means that invisible things still exist in the domain, and all $ watchers will still be triggered.

Ng-if and ng-switch are actually completely removed from the DOM, and the corresponding fields are also removed. The performance difference is obvious.

9. Update cycle problems

9.1 bind

Minimize your binding workload. In Angular 1.3, there is a new binding Syntax: {: scopeValue }}. It is executed only once by the domain and is not added to the watcher array to be monitored by the monitor ).

9.2 $ digest () and $ apply ()

Scope. $ apply is a powerful tool that allows you to introduce external values to Angular. Essentially, it triggers all Angular events (such as ng-click ). The problem is that scope. $ apply will start from the root domain $ rootScope and traverse all domain chains to trigger each domain.

Scope. $ digest only executes the specified domain and its related domain. The two performance differences are self-evident. In the compromise, no domain is triggered until the next update cycle is updated.

9.3 $ watch ()

Scope. $ watch () has been discussed in many scenarios. Basically, scope. $ watch is a sign of poor design. If you want to create an observer. Remember to try to unbind it. You can unbind the $ watch return function.

Var unbinder = scope. $ watch ('scopevaluetobewatcher ', function (newVal, oldVal) {}); unbinder (); // This line removes watcher from $ watchers.

If you cannot unbind it earlier, remember to unbind it in $ on ('$ destroy.

9.4 $ on, $ broadcast and $ emit

Like $ watch, they are slow events and (possibly) traverse the entire scope. They may be like GOTO, making your program unable to be debugged. Fortunately, like $ watch, they can all be unbound when they are not needed at all. For example, in $ on ('$ destroy.

9.5 $ destroy

As mentioned above, you should unbind all your event listeners in $ on ('$ destroy') and cancel any $ timeout instances, or any other Asynchronous interaction. This is not just to ensure security. It also allows your domain to be reclaimed faster. Otherwise, they will always run in the background. Directly clear the CPU and RAM.

In addition, unbinding event listeners on the DOM is also very important. Otherwise, it is likely to cause memory leakage in older browsers.

9.6 $ evalAsync

Scope. $ evalAsync is a powerful tool. It can be executed in the current domain without triggering domain updates. EvalAsync can greatly improve the performance of your web page.

10 command Problems

10.1 isolated domains (Isolate Scope) and Transclusion

Domain isolation and Transclusion are the most exciting features of Angular, which are core components of Angular.

However, there are also some trade-offs. commands cannot directly create a domain that replaces the elements of their parent group. Through an isolated domain or Transclusion, we can create a new object to track and add a new monitor, but this also reduces the performance of the application. Before adding an account, consider whether this is necessary.


10.2 editing and renewal cycle

The compile function of Directive is perfect for DOM operations before the domain is attached (for example, binding events ). An important performance aspect is that the elements and attributes of the compile function are presented in the original html template. It will only run once and will be used directly next. Another important point is the difference between prelink and postlink. Prelink is executed from the external server. Postlinks is executed from the inside out. Prelink has better performance because it does not produce a second update cycle. However, the sub-element DOM has not yet been created.


11 DOM events

Angular provides many predefined DOM Event commands. Ng-click, ng-mouseenter, ng-mouseleave, and so on. These events are executed when scole. $ apply () is called. Another more efficient way is to bind addEventListener directly to the DOM and try to use scope. $ digest

Optimized instances


Testing an application framework is indeed a serious challenge. When a user clicks any word in the log, We need to search for relevant information, and there are countless elements that can be clicked on the page; we want the paging function of logs to be instantly fed back. In fact, we have obtained the log data on the next page in advance, so the update of user interfaces becomes a bottleneck. If AngularJS is used to directly implement the log view page feed function, it takes 1.2 seconds, however, it can be reduced to 35 ms after careful optimization. These optimizations have proved to be applicable in other parts of the application, and are also adaptable to AngularJS. But we must break some rules to implement our ideas and discuss them later.

A log demo updated by Github

An AngularJS log viewer

In essence, the log view is a list of log messages, each word can be clicked. Therefore, to add Angular commands to DOM elements, the simple implementation is as follows:

 {{token | formatToken}}  

It is normal to have thousands of tokens in a single page application. In early tests, we found that it would take several seconds to execute JavaScript to go to the next page of the log. Even worse, the latency of unrelated operations (such as clicking the navigation drop-down box) is not light. AngularJS's great God said it is best to control the number of data element bindings below 200. For a word that is an element, we have already exceeded this number.

Analysis:

Using Chrome's JavaScript profiler tool, we can quickly locate two delays. First, each update takes a lot of time to create and destroy DOM elements. If the new view has different rows or any row has different numbers of words, angular's ng-repeat command will create or destroy DOM elements, which is too costly.

Second, each word has its own change watcher, and AngularJS will watch these words. Once you click the mouse, it will be triggered, which is the culprit affecting the delay of irrelevant operations (drop-down menu navigation.

Optimization #1: caching DOM elements

We have created a variation of the ng-repeat command. In our version, if the number of bound data is reduced, the excess DOM elements will be hidden rather than destroyed, if the number of elements increases later, we will reuse these cached elements.

Optimization #2: Aggregate watchers

Most of the time spent calling change watchers is wasted. In our applications, data binding on specific words will never change unless the entire log message changes. To achieve this, we created a command "hides" to hide the change watchers of child elements, which will be called only when a specific parent element expression is modified. In this way, we avoid the overall change watchers caused by every mouse click or other small changes (to achieve this idea, we slightly modified the abstract layer of AngularJS, let's talk about it later ).

Optimization #3: deferred element Creation

As mentioned above, we have created a separate DOM for each word in the log. We can use a single DOM element in each row to get the same visual presentation; other elements are created in response to the mouse click operation. Therefore, we decided to postpone the creation of this part. We only need to create it when the mouse moves to a row.

To achieve this, we have created two versions for each row. One is a simple text element to display the complete log information, and the other line is a placeholder, used to display the final effect after each word is filled. This placeholder is hidden at the beginning. It is displayed only when the mouse moves to that line, while the line of simple text is hidden at this time. The following describes how to fill word elements with placeholders.

Optimization #4: Avoid monitoring hidden elements

We have created another command to prevent monitoring of hidden elements. This command supports optimization #1. Compared with the original data, we have more hidden DOM nodes, therefore, you must eliminate the monitoring of multiple DOM nodes. This also supports optimization #3, making it easier to postpone the creation of word nodes. It is not until the tokenized version of this line of data appears that we will create it.

The following code shows all the optimized commands. The custom commands are displayed in bold.

 

{{logLine | formatLine }}

{{token | formatToken }}

Sly-repeat is a variation of ng-repeat. It is used to hide multiple DOM elements rather than destroy them. sly-evaluate-only-when prevents internal change of watchers unless the "logLines" variable is modified, sly-prevent-evaluation-when-hidden is mainly responsible for displaying hidden p only when you move the cursor over the specified row.

AngularJS's control over encapsulation and separation is shown here. We have made complicated optimizations but have not affected the template structure (the code displayed here is not the code in the real product, but he shows all the points ).

Result:

Let's take a look at the effect. We have added some code to measure the effect, starting from clicking the mouse until the Angular's $ digest loop ends (meaning that the DOM update is over ).

The performance of clicking the next page is measured by using Tomcat logs, and Chrome on MacBook Pro is used in the environment. The results are shown in the following table (each data is the average value of 10 tests ):

Data already cached Obtain data from the server
Simple implementation 1190 MS 1300 MS
After Optimization 35 MS 201 MS

This data does not include the time that the browser uses for DOM layout and re-painting (after JavaScript Execution is completed), about 30 ms each time. Even so, the effect is obvious; the response time of the next page decreases from 1200 ms to 35 ms (if rendering is 65 ms ).

The data in "getting data from the server" includes the time when we use AJAX to get log data from the backend. This is different from clicking the next page, because we prefetch the log data of the next page, but it may be applicable to 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.