Friday, April 10, 2020

18 Quick Tips for Improving AngularJS Performance

AngularJS is an open source JavaScript framework developed and maintained by Google. The tool provides everything you need to create and manage dynamic frontends for web applications. Its modular approach to web design and massive support community make AngularJS a popular tool among professional developers. In fact, AngularJS powers some of the web’s most high traffic websites including Google and Virgin America. This guide will serve as an introduction to AngularJS and offer tips on how to improve AngularJS performance.

What is AngularJS?#

AngularJS was created to simplify the complex process of building and managing JavaScript applications. Based on the Model-View-Controller, or MVC, programming structure, AngularJS is especially useful for creating single page web apps. With a JavaScript library based on standard JS and HTML, AngularJS automatically takes care of things like DOM manipulation and AJAX glue that would otherwise have to be coded by developers. The tool provides modular building blocks of JavaScript for developers to mix, match and test. AngularJS can quickly be added to any HTML page with a simple tag.

The pros and cons of AngularJS#

A few features set apart AngularJS from its competition including:
  • Simplified two-way data binding. AngularJS allows you to bind data to HTML using expressions, and AngularJS directives let developers extend their HTML functionality to create new constructs. Things like DOM manipulation and data binding code get condensed into simple elements that can be quickly and easily embedded in HTML templates.
  • Since AngularJS was designed to be a highly versatile framework, it can be used to create almost any type of web application. If you’re building a dynamic single page app, there’s likely no better alternative.
  • AngularJS is part of the MEAN software bundle, which also includes MongoDBExpress.js, and Node.js. Therefore, it allows you to manage both the front and backend of projects using only JavaScript. Alternatively, Ruby on Rails makes a great complimentary backend. ASP.NET and C# also pair well with AngularJS.
  • Because AngularJS was built with a functionality-first mindset, it’s best suited for a top-down development process. The modular nature of AngularJS makes it easy to divide the labor in large-scale projects among different teams. It also greatly simplifies the testing and debugging process. Because they prioritize using a minimal amount of code, AngularJS applications tend to be compact and easy to edit.
Nonetheless, there are some things that you should take into consideration when deciding if AngularJS is the right fit for your project.
  • First and foremost, AngularJS is considered to be very opinionated, which means that it imposes structure on developers. For novice and even expert programmers, this is generally good news. AngularJS was designed to be as user-friendly as possible, so its tools are fairly intuitive. However, developers who crave more flexibility may find themselves having to “work around” the framework.
  • For some projects, using AngularJS might be overkill. Lightweight frameworks such as Backbone.js may be a better option for static websites. AngularJS also isn’t equipped to handle data-intensive DOM manipulation since it relies on “dirty checking” to manage DOM changes, which means that any alterations to variables trigger a DOM update. Although that’s not an issue for many websites, it could cause applications like GUI editors and video games to lag.
  • AngularJS also struggles to support high-traffic photo galleries, which is why Instagram wasn’t built on the framework. You can work around these performance issues, but it might be better to go with an alternative like React. Otherwise, AngularJS is capable of supporting forms with high levels of user interaction; after all, it does power Gmail.

AngularJS optimization tips#

AngularJS has plenty of built-in optimization tools, but performance complaints still plague the framework. If you don’t have the massive infrastructure that Google has, you might need to implement some best practices to improve your AngularJS application’s performance.
Whether you know you’re in need of a performance boost, or if you just want to see if there’s room for improvement, here are some tips for getting your AngularJS apps up to speed:

1. Keep an eye on your digest cycle#

The digest cycle of your AngularJS app is a good indicator of its performance. Think of the digest cycle like a loop that checks for changes to variables being monitored. The shorter the digest cycle, the faster your application will run.

2. Limit your watchers#

Speaking of which, any time you introduce data-bindings, you create more $$watchers and $scopes, which prolongs the digest cycle. Too many $$watchers can cause lag, so limit their use as much as possible.

3. Use one-time binding, if possible#

If you’re using an older version of AngularJS, you may be able to take advantage of one-time binding. To do so, just add a double-colon before the value. If applied correctly, the value will resolve once and then disappear from the watchers list. It’s important to note that the one-time binding feature, which was introduced in AngularJS 1.3, is not available in Angular 4.0.

4. Use scope.$evalAsync#

If you try to manually activate the digest cycle while it’s already running, you could get an error. To prevent this from happening, use scope.$evalAsync instead of $apply when you need to initiate the digest cycle. It queues up operations to be executed at the end of the current cycle without setting off a new one.

5. Use Chrome DevTools Profiler and Timeline#

Both the DevTools Profiler and the Timeline tools can help you find performance bottlenecks to guide your optimization efforts. Read our in-depth guide on Chrome DevTools.

6. Limit DOM access#

Accessing the DOM can get expensive, so keep your DOM trees small. Don’t modify the DOM if you can help it, and don’t set any inline styles to avoid JavaScript reflow.

7. Disable CSS class and comment directives#

When creating a directive, you can designate it to be used as an element, attribute, CSS class or comments. If you don’t need CSS class and comment directives, disable them for a performance boost.

8. Disable debug data#

Tools like Batarang and Protractor rely on the data about binding and scopes that AngularJS attaches to DOM elements. Therefore, when you’re done debugging, disable the extra data so that it doesn’t drag your application down.

9. Use Lodash#

Lodash lets you quickly rewrite your application’s logic to improve upon the built-in AngularJS methods and enhance your application’s performance. If your web app doesn’t use Lodash, you can rewrite the methods yourself using native JavaScript.

10. Use ng-if or ng-switch instead of ng-show#

The directive ng-show simply toggles the CSS display on or off for a specified element.
To remove an element from the DOM, you must use ng-if or ng-switch.

11. Avoid ng-repeat when possible#

Overuse of the ng-repeat directive can drastically drive down performance. Fortunately, there are alternatives. For instance, rather than employing ng-repeat to render a global navigation, you could make your own by using the $interpolate provider to render your template against an object before converting it into a DOM node.

12. Use $watchCollection#

When you’re using $watch, two parameters are great, but three’s a crowd. Adding a third parameter forces AngularJS to run deep checking, which eats up a lot of resources. The developers were nice enough to include a work around: $watchCollection. It behaves as a third parameter for $watch, yet it just checks the first layer of each object’s properties, so it doesn’t slow things down as much.

13. Use $cacheFactory#

If you need to store data that you might need to recalculate later, use the $cacheFactory directive. It works like any other memoization method.

14. Use console.time#

If you’re having problems debugging, console.time (Chrome DevTools) is an excellent tool for measuring execution times and other performance benchmarks.

15. Debounce ng-model#

Debouncing inputs using the ng-model directive can limit the digest cycle. For example, applying ng-model-options="{debounce:200}" ensures that the digest cycle doesn’t run more than once every 200 ms.

16. Use $filter#

AngularJS runs DOM filters twice during each digest cycle: first to detect changes, and then to update values that have changed. To save some time, the $filter provider allows you to preprocess data before it gets sent to the View and thus skips the DOM parsing process.

17. Tight scoping#

Keep your variables scoped tightly so that the JavaScript garbage collector can free up some memory every now and then.

18. Pagination or infinite scroll#

If all else fails, you can lower the number of elements that get looped over by implementing pagination or infinite scroll. AngularJS even has a directive called ngInfiniteScroll for that purpose.
It’s always more efficient to employ best practices from the beginning rather than to keep going back and making changes. Before you start coding, think carefully about how you can limit bindings, watchers and expensive directives like ng-repeat. Consult the official AngularJS docs for troubleshooting and additional help getting started.

AngularJS performance and testing tools#

You can find a number of tools dedicated to improving the testing and performance of AngularJS apps. Here are a few of the best options:

1. Protractor#

protractor
Protractor comes straight from the Angular team. This software bundle allows you to run automated end-to-end testing with ease. Because Protractor is built on top of webDriverJS and Selenium server, it boasts all of their features, which means you can use the Selenium grid feature to simultaneously run tests in multiple browsers. You can write your own test suites using Jasmine or Mocha.

2. WebDriverIO#

webdriverio
As an implementation of W3C webDriver API, WebDriverIO is more flexible than WebDriverJS. Its command line interface makes setting up tests easy enough for non-programmers to figure out. WebDriverIO users benefit from excellent support and an active developer community.

3. NightwatchJS#

nightwatchjs
NightwatchJS is also a custom implementation of W3C webdriver API. Easy to extend and customize, this tool comes with its own testing framework and assertions mechanisms, yet it lacks the level of support that WebDriverIO and Protractor have.

4. TestingWhiz#

testing whiz
Dynamic commands allow TestingWhiz to sync with varying server wait times to provide accurate end-to-end testing of Angular apps. The codeless scripting feature makes TestingWhiz highly popular among non-programmers.

5. Batarang#

batarang
The Batarang tool is a Chrome Extension created by the Angular team to make debugging easier. It boasts several handy features, but its most useful for keeping track of performance benchmarks.

Summary#

As a final note, also considering using a CDN to speed up your AngularJS assets. This will optimize the load time for visitors around the globe thanks to smart caching.
Although there are dozens of JavaScript frameworks these days, AngularJS remains a favorite for many, so it won’t be going anywhere anytime soon. To get the most out of your applications, you should make AngularJS optimization a regular habit. Fine-tuning your application’s performance might allow you to provide more content while using less code, which frees up resources that could be better spent elsewhere.
  • TAGS

10 Tricks to Optimize Your Angular App

1. ChangeDetectionStrategy.OnPush

Angular utilized Zone.js to monkey-patch each asynchronous event, so whenever any event occurs Angular runs change detection over its component tree.
Now, this would very easily lead to low performance if the CD runs when the data hasn’t deeply changed but has referentially changed. How? What is deeply changed?
Components have inputs they use to receive data from their parent components. When an async event occurs, Angular parses the component tree and checks the input for any difference from its previous value. This checking for any differences is done via the strict equality operator. This operator checks for reference change in the component’s inputs, that’s a new memory allocation was done for the input’s current values.
With this Angular brought change detection strategies: Default and OnPush.
This OnPush change detection strategy disables CD to be run on a component and its children. When the app bootstraps, Angular runs CD on the OnPush component and disables it. On subsequent CD runs, the OnPush component is skipped along with its children components in the subtree.
CD will be run on the OnPush component only if the inputs have referentially changed.

2. Detaching the Change Detector

This is done by the use of the ChangeDetectorRef class.
export abstract class ChangeDetectorRef {
  abstract markForCheck(): void;  abstract detach(): void;  abstract detectChanges(): void;  abstract checkNoChanges(): void;  abstract reattach(): void;
}
See the methods:
markForCheck: When a view uses the OnPush (checkOnce) change detection strategy, explicitly marks the view as changed so that it can be checked again. Components are normally marked as dirty (in need of rerendering) when inputs have changed or events have fired in the view. Call this method to ensure that a component is checked even if these triggers have not occurred.
detach: Detaches this view from the change-detection tree. A detached view is not checked until it is reattached. Use in combination with detectChanges() to implement local change detection checks. Detached views are not checked during change detection runs until they are re-attached, even if they are marked as dirty. detach
detectChanges: Checks this view and its children. Use in combination with detach to implement local change detection checks.
checkNoChanges: Checks the change detector and its children, and throws if any changes are detected. Use in development mode to verify that running change detection doesn’t introduce other changes.
reattach: Re-attaches the previously detached view to the change detection tree. Views are attached to the tree by default.
Example, we have this component:
@Compoennt({
    ...
})
class TestComponent {
    constructor(private changeDetectorRef: ChangeDetectorRef) {
        chnageDetectorRef.detach()
    }
}
We called the detach from the constructor because that’s the initialization point so the component is detached from the component tree at startup. CD runs on the entire component tree won’t affect TestComponent. If we change a template-bound data in the component, we need to reattach the component, so the DOM is updated on the next CD run.
This does that:
@Component({
    ...
    template: `<div>{{data}}</div>`
})
class TestComponent {
    data = 0
    constructor(private changeDetectorRef: ChangeDetectorRef) {
        changeDetectorRef.detach()
    }    clickHandler() {
        changeDetectorRef.reattach()
        data ++
    }
}

3. Local Change Detection

With the TestComponent like below:
@Component({
    ...
    template: `<div>{{data}}</div>`
})
class TestComponent {
    data = 0
    constructor(private changeDetectorRef: ChangeDetectorRef) {
        changeDetectorRef.detach()
    }
}
We can update the data-bound data property and use detectChanges method to run CD only for the TestComponent and its children.
@Component({
    ...
    template: `<div>{{data}}</div>`
})
class TestComponent {
    data = 0
    constructor(private changeDetectorRef: ChangeDetectorRef) {
        changeDetectorRef.detach()
    }    clickHandler() {
        data ++
        chnageDetectorRef.detectChnages()
    }
}
The clickHandler method will increase the data value by one and call detectChanges to run CD on the TestComponent and its children. This will cause the data to be updated on the DOM while still being detached from the CD tree.
Local CD is run from the component down to its children, unlike the global CD that runs from the root up to the children.
This will be a huge performance if the data variable updates every second.

4. Run outside Angular

Angular has this feature that enables us to run code units outside this Angular zone. Now, in this outside-Angular zone, async events are no longer picked up by NgZone/Zone, so any async event emitted no CD is run for it. This means that the UI will not be updated.
This is very useful if we are running a code that upsets the UI every second. You will see that it is optimal to leave out updating the UI first, then wait till when you want to display the data you re-enter ng Zone.
@Component({
    ...
    template: `
        <div>
            {{data}}
            {{done}}
        </div>
    `
})
class TestComponent {
    data = 0
    done
    constructor(private ngZone: NgZone) {}    processInsideZone() {
        if(data >= 100)
            done = "Done"
        else
            data += 1
    }    processOutsideZone() {
        this.ngZone.runOutsideAngular(()=> {
            if(data >= 100)
                this.ngZone.run(()=> {data = "Done"})
            else
                data += 1            
        })
    }
}
processInsideZone runs the code inside the Angular, so the UI is updated when the method is run.
processOutsideZone runs the code outside the ng Zone, so the UI is not updated. We want to update the UI to show “Done” when the data is equal or past 100, we re-enter ngZone and set the data to “Done”.

5. Use pure pipes

Just imagine we have a function in a @Pipe that takes a good deal amount of time before producing a result.
function bigFunction(val) {
    ...
    return something
}@Pipe({
    name: "util"
})
class UtilPipe implements PipeTransform {
    transform(value) {
        return bigFunction(value)
    }
}
We will see that this function will hang the main thread which runs the UI and will make it laggy for the users. To make it worse, the pipe is called every second that will be one hell of an experience for the users.
To reduce the number times this pipe is called, we have to note first the behavior of the pipe, if it does not change data outside its scope (outside the pipe), ie the pipe is a pure function. We cache the results and return them when next the same input occurs.
So no matter how many times the pipe is called with an input, the bigFunction is called once and the cached results are just returned on subsequent calls.
To add this behavior, we need to set the pure flag in the @Pipe decorator object literal argument to true.
function bigFunction(val) {
    ...
    return something
}@Pipe({
    name: "util",
    pure: true
})
class UtilPipe implements PipeTransform {
    transform(value) {
        return bigFunction(value)
    }
}
With this, we tell Angular that this pipe is pure and doesn’t side-effect, so it should cache the outputs and return them when the inputs occur again.
We see that with this, for any inputs the bigFunction is computed once and cached, subsequent calls with the same inputs will skip recomputing the bigFunction and return the cached results.

6. Use trackBy option for *ngFor directive

Internally, ngFor uses the differs to know when there is a change in the iterable, so it can re-render. the differs uses the strict reference operator === for this, which looks at objects references (ie memory address).
Couple this with the immutability practice, we will see that we will break objects references which will cause the ngFor to continually destroy and re-create the DOM on each iterable.
This will not be an issue for 10- 100 elements in an iterable, but going to 1000 — ~, that will seriously impact the UI thread.
ngFor has an option, trackBy (or I would say its an option for the Differs) that it uses to track elements identity in an iterable.
This will cause it to enable the dev state his identity in the iterable for the Differ to track. This will prevent the whole DOM from being constantly destroyed and re-created.

7. Optimize template expressions

We often run functions in templates:
@Component({
    template: `
        <div>
            {{func()}}
        </div>
    `
})
class TestComponent {    func() {
        ...
    }
}
Now, this func will be run when CD is run on the TestComponent. Also, this func will have to complete before the CD and other codes will move on.
If the func takes a long time to finish, it will result in a slow and laggy UI experience for the users because the func will have to finish before other UI codes will be run. We see that template expressions must finish quickly, if a template expression becomes highly-computational, then caching should be employed on it.

8. Web Workers

Now, if the non-UI algorithm gets heavy we will see that it will impact the UI thread slowing it down. Web Worker is a feature added that enables us to create and run code in another thread. Yes, another thread
Using a self-plagiarism from my previous article Angular Performance: Web Worker :
using Web Workers in Angular, its setup, compiling, bundling and code-splitting were made easy by the CLI tool.
To generate a Web Worker, we run the ng g web-worker command:
ng g web-worker webworker
This will generate webworker.ts file in the src/app of an Angular app. The web-worker tells the CLI tools that the file would be used by a Worker.
To demonstrate how to use Web worker in Angular to optimize its performance. Let’s say we have an app that calculates Fibonacci numbers. Finding Fibonacci numbers in the DOM thread will kinda impact the UI experience because the DOM and the user interactions would freeze until the number is found.
Starting, our app would be like this:
// webWorker-demo/src/app/app.component.ts@Component({
    selector: 'app',
    template: `
        <div>
            <input type="number" [(ngModel)]="number" placeholder="Enter any number" />
            <button (click)="calcFib">Calc. Fib</button>
        </div>
        <div>{{output}}</div>
    `
})
export class App {
    private number
    private output
    calcFib() {
        this.output =fibonacci(this.number)
    }
}function fibonacci(num) {
    if (num == 1 || num == 2) {
        return 1
    }
    return fibonacci(num - 1) + fibonacci(num - 2)
}
Calculating Fibonacci numbers is recursive, passing small numbers like 0–900 would have no performance impact. Imagine passing ~10,000. That’s when we will begin to notice performance drag. Like we said the best bet is to move the fibonacci function or algorithm to execute in another thread. So no matter how large the number is, it will not be felt in the DOM thread.
So we scaffold a Web Worker file:
ng g web-worker webWorker
and move the fibonacci function into the file:
// webWorker-demo/src/app/webWorker.ts
function fibonacci(num) {
    if (num == 1 || num == 2) {
        return 1
    }
    return fibonacci(num - 1) + fibonacci(num - 2)
}self.addEventListener('message', (evt) => {
    const num = evt.data
    postMessage(fibonacci(num))
})
Now we will edit the app.component.ts to add Web Worker
// webWorker-demo/arc/app/app.component.ts@Component({
    selector: 'app',
    template: `
        <div>
            <input type="number" [(ngModel)]="number" placeholder="Enter any number" />
            <button (click)="calcFib">Calc. Fib</button>
        </div>
        <div>{{output}}</div>
    `
})
export class App implements OnInit{
    private number
    private output
    private webworker: Worker    ngOnInit() {
        if(typeof Worker !== 'undefined') {
            this.webWorker = new Worker('./webWorker')
            this.webWorker.onmessage = function(data) {
                this.output = data
            }
        }
    }    calcFib() {
        this.webWorker.postMessage(this.number)
    }
}
Our code is now kiss emoji here We added ngOnInit lifecycle hook in our component so to initialize the Web Worker with the Web Worker file we generated earlier. We registered to listen to messages sent fro the Web Worker in the onmessagehandler any data we get we will display it in the DOM.
We made the calcFib function to send the number to Web Worker. This below in webWorker would capture the number
self.addEventListener('message', (evt) => {
    const num = evt.data
    postMessage(fibonacci(num))
})
and processes the Fibonacci number then send the result back to the DOM thread. The onmessage we set up in the app.component
ngOnInit() {
        if(typeof Worker !== 'undefined') {
            this.webWorker = new Worker('./webWorker')
            this.webWorker.onmessage = function(data) {
                this.output = data
            }
        }
    }
would receive the result in data then we will display the result in the DOM using the {{output}} interpolation.
During the processing of the Fibonacci numbers, the DOM thread would be left focusing on the user interactions while the webWorker would do the heavy processing.

9. Lazy-Loading

This is very effective, it reduces the amount of bundled file that is loaded at the initial load of the webpage, and only loads the resources that will be used directly in the webpage. All other resources are not loaded. When they are needed by the user, the resources needed are then loaded.
Angular provides a very easy way for us to lazy-load resources. To lazy-load routes in Angular, we do this:
const routes: Routes = [
    {
        path: '',
        component: HomeComponent
    },
    {
        path: 'about',
        loadChildren: ()=> import("./about/about.module").then(m => m.AboutModule)
    },
    {
        path:'viewdetails',
        loadChildren: ()=> import("./viewdetails/viewdetails.module").then(m => m.ViewDetailsModule)
    }
]@NgModule({
    exports: [RouterModule],
    imports: [RouterModule.forChild(routes)]
})
class AppRoutingModule {}
We use the dynamic import to tell Angular routes we want to lazy load. Angular will generate a separate chunk for about and viewdetails. On the initial load of the app, the about and viewdetails chunk is not loaded, when the user wants to navigate to about or viewdetails route, the specified chunk is then loaded.
If the size of the whole non-lazy-loaded bundle is 1MB. Lazy-loading will splice out the about and viewdetails from the 1MB, let’s say they are 300kb and 500kb respectively, we will see that the bundle will be cut down to 200kb more than half of the original size!!!

10. Preloading

Angular has preloading strategy implemented in @angular/router module. This allows us to preload resources, routes/links, modules, etc in Angular apps. The Angular router provides an abstract class PreloadingStrategy that all class implements to add their preloading strategy in Angular.
class OurPreloadingStrategy implements PreloadingStrategy {
    preload(route: Route, fn: ()=> Observable <any>) {
        // ...
    }
}
We specify it as the value for the preloadingStrategy property in the router configuration.
// ...
RouterModule.forRoot([
    ...
], {
    preloadingStrategy: OurPreloadingStrategy
})
// ...
That’s it.

Conclusion

These practices must not be all implemented in your Angular app, It’s just worth knowing each of them and knowing when to apply them.
Also, remember, don’t optimize early. Build the product and then, find out places to optimize.
If you have any questions regarding this or anything I should add, correct or remove, feel free to comment, email or DM me.

Free hosting web sites and features -2024

  Interesting  summary about hosting and their offers. I still host my web site https://talash.azurewebsites.net with zero cost on Azure as ...