5

How to Make Web Applications in Angular More Responsive

 1 year ago
source link: https://devm.io/angular/angular-web-apps-more-responsive
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Angular tutorial — part 7

How to Make Web Applications in Angular More Responsive


These days, a web application’s performance has become more important. Many companies are moving their native desktop application to the web browser. The advantages outweigh the disadvantages. The application can be called up on any operating system running a browser. We no longer need to write code specific to one platform - this is the “write once, run everywhere” approach.

With technologies like Electron, you can also generate web apps as a native-like desktop app. For example, well-known apps like Slack, Microsoft Teams, and Visual Studio Code are nothing more than web applications wrapped in Electron. Updating software is also easier. The latest version of the web application is always downloaded from the web servers.

As a result, web browsers as executing environments are becoming increasingly more important. Now, you should make sure that web applications feel just like a real native desktop application. This creates new challenges. The application must download quickly from a web server (even with a poor Internet connection), and interactions in the application should have a short waiting time. Preferably, everything should be highly reactive and in real-time.

Frameworks like Angular support developers by helping implement established best practices on the web. In this article, I’d like to discuss several of these best practices. We will look at Angular Load Performance and Angular Runtime Performance.

As the name suggests, the former is about how fast our web browser can load an Angular application over the network. Here, the size of the files that will be transferred is especially important. In Angular Runtime Performance, we look at how quickly our application can respond to user interactions. For example, this means how quickly the web application displays new content when the user clicks on a navigation item.

As I mentioned earlier, the Angular framework takes a lot out of this process. In the end, everything is transpiled to JavaScript. No one can stop us from doing Document Object Model (DOM) manipulations in an Angular component on our own with plain JavaScript. But, if we do that, we are bypassing the API that Angular provides. As a result, we are leveraging all the mechanisms Angular provides us for support. Unfortunately, this often happens far too quickly and usually without us knowing. In this article, I want to highlight the specific pitfalls and best practices when dealing with the Angular API.

Angular Load Performance

With the era of single page applications, functionality for rendering HTML has been moved to web browsers. But this also means that all of the JavaScript code responsible for this needs to already be downloaded in the web clients. This is also referred to as Fat Clients. Looking at our Angular application, we see that several TypeScript, HTML, and styling files need to be delivered to our web browser. For now, the TypeScript files need to be transpiled into JavaScript. But even then, there are still tens of files that the browser needs to request from our web server. It would be better if we could only generate one JavaScript file from them, so that just one HTTP request would need to be sent.

Meaningfully linking together multiple files to create a single file is called “bundling”. Of course, this concept is nothing new. Bundling has existed before frameworks like Angular, React, and Vue and there are a variety of tools you can use for this. Angular builds upon webpack. Naturally, what sounds simple is actually much more complex. Component dependencies need to be visible even after merging. Name conflicts, which can happen in the entire file, need to be resolved.

webpack helps us even more. Every byte counts when you are transferring data. This includes long variable or function names. But because we don’t want to (and shouldn’t) name our variables only a, t, or m., a tool called “Uglify” does that for us too. It makes our code absolutely unreadable and also minifies it. With these two tools, we are already one big step ahead towards making our initial load JavaScript bundle as small as possible. That’s what Load Performance is all about.

In our final JavaScript bundle, there’s often still code we don't need. In many cases, we install mass amounts of npm packages and add its JavaScript code to our own code, even though we rarely need all the module’s functionality (Listing 1).

Listing 1

// calc.js
export add = (a, b) => a + b;
export square = (a, b) => a * b;
 
// app.js
import { add } from './foo';
console.log(add(11, 10));

As you can see in Listing 1, we only call our module’s add function. All the code for the square function isn’t needed here, so it's best to leave it out of our initial bundle (Listing 2).

Listing 2

// main-bundle.js
export add = (a, b) => a + b;
console.log(add(11, 10));

This principle is called Tree Shaking. Modern JavaScript build tools like esbuild, Snowpack, or webpack used by the Angular CLI have implemented this feature.

In order to get the full advantage out of Tree shaking, you need to keep a few things in mind. There are different formats for defining modules in JavaScript. The most well-known are CommonJS and ES Modules. CommonJS is traditionally used for Node.js modules, but isn’t standardized. However, ES Modules are more modern and standardized. Most frontend packages use the ES Modules format, and unfortunately, there are still modules following the CommonJS format. But why is that so bad?

The main problem is simply that CommonJS modules don’t support Tree Shaking. For example, if we want to import a UI module that has UI components for a header, footer, and button but we only need the button, all of the code for the header and components will still load into my application (Listing 3).

Listing 3

// ES Module Format
import foo from "main";
export bar;
 
// CommonJS Format
const foo = require("main");
module.exports = bar;

One of the most important decision-making criteria should be which module format the npm package complies with. Luckily, most developers already offer packages in several module formats. When building our application, The Angular CLI already helps us detect modules in the unwanted CommonJS format. If needed, we can configure this warning in angular.json and allow the CommonJS module format to certain libraries (Listing 4).

Listing 4

"build": {
  "builder": "@angular-devkit/build-angular:browser",
  "options": {
    "allowedCommonJsDependencies": [
      "lodash"
    ]
    ...
  }
  ...
},

What I’ve mentioned so far is really more of a web development foundation. It should always be considered, and for now, it’s independent from the frontend framework you use. So what can we specifically do in Angular to reduce our initial JavaScript bundle?

Tree Shakeable Providers

I already introduced Angular Services in the third part of this series. I need to provide them to inject services into my component. In Angular 6, the Tree Shakeable Providers were introduced.

This feature guarantees that only the services a component actually needs (injected) are generated in our initial bundle. All we need to create a service as a Tree Shakeable is to use the providedIn attribute inside the @Injectable() decorator.

Listing 5

// api.service.ts
import { Injectable } from '@angular/core'
 
@Injectable({
  providedIn: 'root'
})
export class ApiService { }

This way, we can make sure that this service’s JavaScript code only ends up in our bundle when it’s used.

Ahead-of-Time Compilation

Often, the Angular-specific syntax in our HTML templates means our build tools cannot fully parse the code. Of course, this blocks Tree Shaking, since it isn’t possible to parse with certainty which directives the template references. The Ahead-of-Time compiler hooks in beforehand and compiles the template code into TypeScript or JavaScript code. Then this can be completely analyzed again with our tools. It’s recommended that you always use the Ahead-of-Time compiler at least in the production build configuration. This is the default for all new Angular projects, but can also be customized in angular.json.

Lazy...


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK