Lazy loading can do that?
Tags: angular, chromedevtools, optimization
Published: 2019-06-06 16:26:25

---
title: Lazy loading can do that?
description: Optimizing Angular
published: true
tags: angular,chrome-dev-tools,optimization
canonical_url: 
---

![](https://cdn-images-1.medium.com/max/592/1*I7IBTjR9RETcv27qtqrCVA.png)<figcaption>Speedy angular.</figcaption>

## Optimizing Angular

Update 7 June '19: Seems the bit about the shared module is incorrect. Angular compiler actually tree shakes unused components and does not tree shake unused providers (services). See detailed discussion [here](https://medium.com/@gparlakov/after-a-bit-of-research-heres-the-result-in-this-github-repo-1804df5e8373)

>**Optimizing** primarily for snappy user experience! We’ll take a look at what types of lazy loading we can do and how to measure our results while optimizing Angular.

We'll be **optimizing** primarily for snappy user experience. That means **smaller bundles** and **not downloading** code that is **not required**. We want to **ship** the **bytes** that user **needs** and **nothing more**. That way our users will see a snappy and quick to react site. Users have come to expect that.

Disclaimer: **No server-side rendering** will be discussed here. We’ll try to optimize the javascript that gets delivered and evaluated in the browser.

We’ll take a look at what types of lazy loading we can do and how to measure our results while optimizing Angular.
Here is the plan: We’ll measure first. We’ll create hypotheses. Then we’ll apply some optimization, measure and see if the hypothesis is correct or not.

I’ve created a small example app to exercise the optimizations. Find it at [https://github.com/gparlakov/optimizing-angular](https://github.com/gparlakov/optimizing-angular)

#### Measurement:

We’ll use these tools:

[Webpack bundle analyzer](https://www.npmjs.com/package/webpack-bundle-analyzer). It’s a tool that takes the output from `ng build --stats-json` command and presents it in a very nice graphical interface. It uses the `stats.json` file from the output . We can see how many bundles we produce, what goes where and the size of them (Stat — [raw](https://www.npmjs.com/package/webpack-bundle-analyzer#stat), Parsed — [processed](https://www.npmjs.com/package/webpack-bundle-analyzer#stat) [_like after minification]_ and [Gzipped](https://www.npmjs.com/package/webpack-bundle-analyzer#gzip)). Here’s an example:

ng build --prod --stats-json  
webpack-bundle-analyzer dist\stats.json

![](https://cdn-images-1.medium.com/max/1024/1*ex-MzdA2u5TBGAbFsKlAAw.png)<figcaption>A screenshot of the webpack-bundle-analyzer tool report. 1. Top left — Checked the ‘Show content….’ checkbox to be able 2. Top center — main bundle . 3. Bottom right — app’s components.</figcaption>

[Webpack Bundle Analyzer](https://gparlakov.github.io/optimizing-angular/stats/v1_eager_base_line/report.html)

I the screenshot (or if you visit the above link) you’ll see that main bundle is by far the largest — with 1.17MB Parsed (how much disk it takes up) and 328KB Gzipped (estimation\*\*). It includes the angular runtime and libraries (@@angular - core.js, forms.js), ethers\* andbip39\* , primeng/components — our UI components and a bunch of smaller and larger js files all bundled into one — including src where our apps custom code is. This answers the question — how much javascript do we ship to the browser in order to draw our app.

_\*These are libraries used for crypto stuff. I’ve selected them because I know they have a large footprint. They will help show one particular aspect of optimizing._

[The chrome dev **network**  tool:](https://developers.google.com/web/tools/chrome-devtools/network/)

![](https://cdn-images-1.medium.com/max/1024/1*AdNolpqiSA0CErJDoSBnwQ.png)<figcaption>We want to optimize the load time for the end user.</figcaption>

It shows a summary of how much and how fast the resources (filtered to JS in this case — notice the JS selected in the filter bar above) have loaded.  
Seems this is already a quick loading app. But considering there are three pages with minimal content I think we can do way better. 

\*\*There is some misalignment between the webpack-bundle-analyzer report (showing 328KB main) and the network tool (showing 373 KB). Since this is served by the angular development server (ng serve — prod) I’m guessing different versions of gzip is the explanation. Anyway — we’ll take the **size** of the **bundle** as seen in the report as **indicative** to the **actual bytes** being **transferred** and factor in that the concrete implementations (nginx, IIS, Apache, etc.) might result in a small difference in size when squeezing our bundles.

[And the chrome dev tools **performance** tool](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/):

![](https://cdn-images-1.medium.com/max/1024/1*SpE1fiXIQRMFurLtqRfQ1Q.png)<figcaption>Screenshot from the Chrome performance tool. We can see the network's statistics (top) and what our main thread is doing (bottom).</figcaption>

From the performance shot above, we can see the **main.d72a….js** bundle takes the bulk of the network and a lot of time getting evaluated.

When **measuring** take care to use the **incognito** window (in Chrome and whatever the other browsers call it), or disable any extensions that might interfere. In my case, I had two extensions that added about 700 ms to the measured load time. So I did all my measurements incognito.

All measurements are done with --prod flag set in the ng build or ng serve commands. Otherwise, we’ll get source maps, unminified javascript and a lot larger bundles altogether.

With that said, this is our baseline:

#### Baseline

All right, we have our baseline:  
**1.17MB** size of **main.js** (328k gzipped)   
**830ms** load time.  
**217ms** evaluating main.js  
Now to the fun part — the optimizing.

#### Lazy load

Hypothesis: lazy load will help speed up the initial bootstrap time and quicken the user experience.

Lazy load is the technique to only load the modules needed and leave the rest on the server until required. For example, our app has a **home** module, **login** module, and **personal** module. Every one landing on the home page needs the **home** module. But not everyone is going to log in or use the personal profile page. So the login and personal modules will remain on the server until the user actually requests to use them. That way we keep the app loading quickly and save the user’s data plan. (Again this example app is small and a bit contrived, but trust me I've got a point).

Initially, we have all our modules and components loaded eagerly at the start of our app. Hence the build only creates one bundle — main.js — and concatenates everything — vendor code and our custom code — into it. Our initial app-routing.module and app.module look like this:

{% gist https://gist.github.com/gparlakov/d55f0befc61a781c57498d41b0ae80a7 %}

In order to instruct our bundler (Webpack) to create separate bundles for personal and login modules we need to not import these modules in our app.module and use the special syntax to load them lazily _(of course that’s the @angular/router’s special syntax telling angular which modules to load lazily)_:

{% gist https://gist.github.com/gparlakov/19b311d56091325c13736c8876450991 %}

This is enough to result in two new bundles that do not get loaded immediately with the first request. And if we do theng build and webpack-bundle-analyzer tool routine we get:

![](https://cdn-images-1.medium.com/max/1024/1*_rT_vW4VRPOJaPCGNk96sg.png)<figcaption>Notice how we’ve more than halved the main.js bundle. Interactive version: <a href="https://gparlakov.github.io/optimizing-angular/stats/v1_eager_base_line/report.html">https://gparlakov.github.io/optimizing-angular/stats/v2_lazy/report.html</a></figcaption>

And if we measure the network we get:

![](https://cdn-images-1.medium.com/max/1024/1*8qGrJQi_5JpZa8R2hY-teQ.png)<figcaption>Screenshot of the chrome network dev tool showing the size of the js downloaded and the load time.</figcaption>

And running the performance tool we get:

![](https://cdn-images-1.medium.com/max/1024/1*uaiCgVf1PiwQSJ_am4IuVA.png)<figcaption>Screenshot with the js execution time 183 ms (it varies a bit per attempt)</figcaption>

**Results**

**455KB** size of **main.js** (126k gzipped) (50% reduction)  
**~600ms** load time. (25% reduction)  
**183ms** evaluating main.js (~20%) reduction

> Hypothesis: lazy load will help speed up the initial bootstrap time and quicken the UX.

Hypothesis is proven.

All right, we are getting somewhere. Keep in mind that when we navigate to /login and/or /personal we’ll have to pay the network and javascript “price”. Still — on our main page we’ve hastened the UX considerably.

#### Large dependencies

Next, we can take a look at **personal** module. Among other things, it allows for creating a crypto wallet. Because that’s what our example app deals with — Crypto! (It may look a bit contrived, but I actually had to deal with a similar setup while optimizing a production app — [https://propy.com](https://propy.com) — where buying real estate with crypto is a thing.)

The **personal** module depends on ethers and bip39 and their dependencies, but is only ever used when the user creates a wallet. This happens generally once per the user's use of our app. Ever. It would be nice if we could instruct Webpack to create a separate bundle for them and only load it when we need it. Turns out [we can.](https://dev.to/gparlakov/webpack-magic-comments-12ck-temp-slug-5766948) In short, there is a syntax we can use when using import() to instruct Webpack to treat these as lazy loaded modules. We need to have our tsconfig.json modules set to esNext (or commonjs ) in order for the Typescript build to succeed.

**Hypothesis** _:_ we can further increase the perceived speed of our app by separating the rarely used and very large dependencies into their own bundle. And only load them when needed.

{% gist https://gist.github.com/gparlakov/bcdcacaed2617b5254874ac97ca32c7d %}

So now running our webpack-bundle-analyzer tool afterng build --prod --stats-json we get:

![](https://cdn-images-1.medium.com/max/1024/1*DmMNPJoLQAyGZB6kh_ObKQ.png)<figcaption>Screenshot showing the bip39_ethers bundle (size 622KB), personal ng (size 26KB) and login (size 8KB). Interactive version: <a href="https://gparlakov.github.io/optimizing-angular/stats/v3-lazier/report">https://gparlakov.github.io/optimizing-angular/stats/v3-lazier/report</a></figcaption>

So now we’ll only ever load that dependency if a user decides they want to create a new wallet.

![](https://cdn-images-1.medium.com/max/1024/1*VTDSIL8mMH3y7tS7YKuy9w.png)<figcaption>Screenshot of the '/personal’ page with the tab ‘Create a wallet’ selected. The bip39_ethers bundle is not downloaded yet.</figcaption>

![](https://cdn-images-1.medium.com/max/1024/1*_JACX6gLBCxmWd1fyc785w.png)<figcaption>Screenshot after the ‘Create me a wallet’ button clicked: `bip39_ethers` bundle is just now downloaded.</figcaption>

We have not changed the size of our initial javascript or page load time, but we saved a lot of users a lot of data by deferring the large dependency, to the last possible moment.

> **Hypothesis** _:_ we can further increase the perceived speed of our app by separating the rarely used and very large dependencies into their own bundle. And only load them when needed.

The hypothesis is proven.

#### **Shared bucket**
Update 7 June '19: After a question resulting in this answer [here](https://medium.com/@gparlakov/after-a-bit-of-research-heres-the-result-in-this-github-repo-1804df5e8373) it seems this part is not correct. In short Angular does tree shake away unused components but not unused providers (services).

Next, I want to mention the shared module/folder. It is a module that gets **all** components that will be shared in our app. In order to keep [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) (I really hate that principle, as it often leads to disaster) we devs try and keep everything reusable. Where to put a shared component? The /shared folder. And since it is in the **/shared** folder, let’s declare and export it in the **shared** module. Makes sense — right?

Monkey see, monkey do and everyone on the team does the same. It’s obviously a shared component. Put it in the **/shared** module. In our app, we have two components in our shared module. I created it in a few hours, alone. One of them — the menu component is and it is a dependency of our [root component’s module](https://github.com/gparlakov/optimizing-angular/blob/19c2687bca586f67d43ac1c32926c0f1615d944b/src/app/app.module.ts#L17).

Imagine a few months of team-work. The **shared** module now has hundreds of declared components. But our app-component only needs one or two of them — the rest are used by the feature modules. Despite that our main module will import and build the components and our main bundle will include **all that code**. So now if you want to get the banana, you get the banana and the monkey, holding it and the whole jungle!

How can we avoid that? There is no easy answer I can give. My team, we have that situation at the time of this writing, and we are dealing with it by:

1. **Awareness ** — telling the whole team that this is an issue and why it’s one.
2. Create **new components** in the **/shared folder** with their **own modules**. Now any module that needs them can import them.
3. **Trim** the fat off the **shared module** by taking components **out** of it and moving them to their **own modules**. One component at a time. It will take time, but it’s worth it.

(What do you do — share in the comments.)

The hypothesis here is that if everyone on the team is aware of the issue and we work together at it, bit by bit we’ll have a solution.

No evidence to prove that, other than that: I see my teammates trying their best to improve on the situation and separating modules every chance they get. I believe we’ll make it work. One module at a time.

#### To recap:

We started with one **eagerly** loaded app and managed to split it into lazy loading modules. By that, we potentially increased the app’s perceived performance, or in technical terms: **snappyness**. We managed to pinpoint and further separate out rarely used and large dependencies. That way saving user’s data. We talked a bit about the ‘shared’ module (bucket, kitchen sink if you prefer) and why it’s an enemy of the ability of the app to get segregated into lazy loaded modules with clean dependency trees.

If you want to try it out, one step or more, you can. I’ve tagged the steps: [1-eager](https://github.com/gparlakov/optimizing-angular/releases/tag/1-eager), [2-lazy](https://github.com/gparlakov/optimizing-angular/releases/tag/2-lazy), [3-lazier](https://github.com/gparlakov/optimizing-angular/releases/tag/3-lazier), [4-deshareify](https://github.com/gparlakov/optimizing-angular/releases/tag/4-deshareify) .

* * *