Differential loading

Differential loading

overview and how it is used in Angular 8+

Browser support considerations have been an ever growing concern of web developers, with new technologies increasingly leaving behind older "legacy" browsers, leaving them unable to handle the advanced syntax used in development today.

However, some of these legacy browsers still have their userbase, who understandably expect websites to work for them just as for those who use modern and frequently upgraded, so called "evergreen" browsers.

In the end, it falls on the developer's shoulder to provide the best experience for every kind of user, and in the case of legacy users, this is done by compiling the code to an older syntax comprehensible to their browsers (compiling ES6 down to ES5 is a typical example in JavaScript), and adding polyfills to the code that serve a similar purpose.

The catch

However, this solution by default means that our compiled bundles will be burdened by the extra size of the polyfills, as well as extra compiling time, not to mention the lower performance of the older code syntax. This can even stack up to a 20% increase in initial load times, which is certainly noticeable, not to mention unnecessary for the vast majority of the users.

It is not exactly fair that they should compromising their user experience for legacy browsers that they do not actually use, but dropping the legacy support would be unfair to others. Now, if only there was a way to combine the best of both worlds...

A different(ial) solution

Enter the method of differential loading: at build, our compiler will check our loading configuration (browserlists, more on that later), cross reference it with our compilation target and if necessary creates two separate versions of our app, one intended for evergreen browsers and one for legacy ones. Later, the API checks the type and version of our user's browser, and decide which bundle to send out.

This means that newer browsers can skip the extra data and perform better without barring legacy users from the full functionality of our application. Since all of this is happening automatically, the developer is also freed from many of the earlier considerations regarding browser compatibility.

It has to be mentioned that building two bundles will increase both building and deployment times, so it is up to the developer whether that is an acceptable trade-off for faster loading times on the user's side.

How do separate bundles work

A new feature type = "module" lets our app differentiate between bundles intended for different browsers, since only later browsers understand this type definition, meanwhile older ones will fall back to to the nomodule script:

bundles.png

However, because of the many different types and versions of browsers out there, adding the correct tags manually would be extremely time consuming. Luckily, a configuration tool called browserlists can help us out: in there we can define the range of browsers we intend to support, which will be used to generate the new bundles if needed.

browserslist can be configured either as an entry in package.json:

package-browserslist

or as a separate .browserslistrc file:

browserslistrc-file

There are many different conditions we can use, with the full list accessible here

Where is Angular in all of this?

Luckily, you might not have to deal with manually setting up differential loading. In the case of Angular 8 or above differential loading is already integrated in the CLI, saving you the effort of configuring it to the compilers.

Note: Angular 7.3 already used es5BrowserSupport, but that could only handle polyfills, and is now deprecated.

Any new Angular project will include the browserslist file with the default settings, working as it should: right out of the box.

We can of course always tweak our browserslist configuration as we please, or completely switch the feature off (by enabling all, even dead browsers). Setting our compiler target to ES5 is also an option, which will mean our differential loading setup will only take care of optional polyfills.

Overall, it is recommended to take advantage of the feature as it can result in a 7-20% loading speed improvement, especially in larger apps.

Hope you enjoyed the brief introduction, and feel free to check out the official Angular documentation or the Angular V8 documentation for a deeper dive.