The 8.0.0 release of Angular (from 28th May 2019) is finally here and this major version release spans across whole platform, including framework itself, Angular Material and the CLI. This update includes a preview of Ivy (Angular’s new compiler and new rendering pipeline), Web Worker support, differential loading (thanks to this, modern browsers will have startup time significantly faster) and many more (full list of changes can be seen on Angular’s official changelog).
An Ivy is a codename for huge work from the Angular team and a foundation for the future of Angular. Ivy is Angular’s next-generation compiler and rendering pipeline. It’s third in line after the original compiler (for Angular 2) and Renderer2 (for Angular 4 and above). Misko Hevery and Kara Erickson (Software Engineers at Google) gave us the first look of Ivy at ngConf-2018. Currently, Ivy is still in Preview mode and is not feature complete, but you can enable it for experimentation and testing. To start a new project with Ivy enabled, use next command with --enable-ivy
flag:$ ng new my-ivy-app ––enable-ivy
And to update an existing project to use Ivy, set the enableIvy
option in the angularCompilerOptions
in your project's tsconfig.app.json
:
{
"compilerOptions": { ... },
"angularCompilerOptions": {
"enableIvy": true
}
}
“Ahead-of-time” (AOT) compilation with Ivy is faster and should be used by default. In the angular.json workspace configuration file, set the default build options for your project to always use AOT compilation.
Ivy is going to have smaller and simpler bundles and faster compilations, as quoted by Angular team. Ivy follows the locality principle, where only one file is compiled at a time. When generating the output, it only looks at a component and its template, not its dependencies. This leads to faster compilation and simplification of process. With locality, there’s more opportunity for meta-programming, like higher order components and the ability to dynamically generate modules, components, or pipes. Ivy uses more efficient tree-shaking, removing unused pieces of your code, which results in smaller bundles and faster load times. Ivy breaks things down into smaller, more atomic functions. These atomic functions make the renderer code much more friendly to tree-shaking, because they generate only the code you need from the template you’ve written.
Web workers are a great way to speed up your application if you do any sort of cpu-intensive processing (like image or video manipulation). They allow you to offload work to a background thread, freeing the main thread to update the user interface. You can add a web worker anywhere in your application. If the file that contains your expensive computation is src/app/app.component.ts
you can add a Web Worker using command:$ ng generate webWorker my-worker
Running this command will:
src/app/app.worker.ts
with scaffolded code to receive messages:
addEventListener('message', ({ data }) => {
const response = `worker response to ${data}`;
postMessage(response);
});
src/app/app.component.ts
to use the worker:
if (typeof Worker !== 'undefined') {
// Create a new worker
const worker = new Worker('./app.worker', { type: 'module' });
worker.onmessage = ({ data }) => {
console.log('page got message: ${data}');
};
worker.postMessage('hello');
} else {
// Web Workers are not supported in this environment.
// You should add a fallback so that your program still executes correctly.
}
Once you have a web worker, you can use it normally in your application and you will need to refactor your code to use it by sending messages to and from it.
Differential loading is a process by which the browser chooses between modern or legacy JavaScript code bundle based on its own capabilities. Until now, it was common to compile applications to old ECMAScript 5, since this JavaScript version runs almost on every browser today. This means that both IE 11 and the web crawler behind the Google search engine can execute the program code successfully. However, the new ECMAScript 2015 and its newer versions are more efficient: these versions allow more compact bundles and the browser can also interpret them more efficiently.
To activate differential loading it is necessary to set an upper and a lower bar for the ECMAScript versions you want to support. The upper bar is entered in the tsconfig.json
file:
{
"compilerOptions": {
...
"module": "esnext",
"moduleResolution": "node",
...
"target": "es2015",
...
}
}
When target is set to es2015
, Angular CLI will generate and label two bundles.
The lower bar, on the other hand, is defined by a browserslist, file which CLI creates in project root when generating a new project and where you can identify how many browsers has to be supported:
> 0.5%
last 2 versions
Firefox ESR
not dead
IE 9-11
In the illustrated case, the browserslist points to ECMAScript 5 browsers with the entry IE 9-11, thereby, the CLI determines the lower bar as this version.
The disadvantage of this process becomes obvious here: the time required for the build process is doubled.
The different browsers can now decide which version of the bundles to load. For this, they receive the script references in the index.html
additions: those pointing to ECMAScript 5 bundles receive the addition nomodule
. This prevents browsers with support for ECMAScript modules, and thereby ECMAScript 2015 and newer, from ignoring the reference. The ECMAScript 2015+ bundles, on the other hand, are implemented by the CLI via type=”module”
. Thus, older browsers will ignore these script tags:
<script src="main-es2015.js" type="module"></script> <!-- ECMAScript 2015+ -->
<script src="main-es5.js" nomodule></script> <!-- Older browsers (IE 11), ECMAScript 5 -->
Thanks to differential loading (as seen below), the bundle sizes can already be optimized immediately.
To create a lazy-loaded module in an Angular app before version 8, you had to do the following:
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
@NgModule({
imports: [
RouterModule.forChild([{
path: '',
loadChildren: './lazy/lazy.module#LazyModule'
}])
})
export class MyModule { }
The value before the hash sign represents the path which leads to the file with the module implementation; the value afterwards stands for the therein contained class. This style of writing also works with Angular 8, but has been deprecated in favor of dynamic ECMAScript imports, as following:
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
@NgModule({
imports: [
RouterModule.forChild([{
path: '',
loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule)
}])
})
export class MyModule { }
In Angular version 8, there is a breaking change in the usage of ViewChild
and ContentChild
. While they were used in earlier versions for a component to request an element not inside a structural directive like ngIf
or ngFor
, the query result was already available in ngOnInit. Otherwise, the program code could access it at the earliest in ngAfterViewInit
(or ngAfterContentInit
for ContentChild
). For elements that were only loaded into the DOM at a later time due to data binding, the program code had to insert ngAfterViewChecked
or, respectively, ngAfterContentChecked
.
As this behavior was confusing, the component must now specify when the resolution should take place:@ViewChild('info', { static: false }) paragraph: ElementRef;
If static has the value true, Angular will try to find the element when initializing the component. This only works if it is not in a structural directive. When using static: false
, the resolution takes places after initiating or refreshing the view.
Updating Angular CLI to version 8 and updating your project to Angular version 8 from version 7 is very easy process, thanks to all the work that has been done in version 8 and the ng update
command, which allows you to update specific versions and dependencies.
Here we provided a checklist that you have to go through to successfully update your Angular application from version 7 to 8:
HttpModule
and the Http
service, switch to HttpClientModule
and the HttpClient
service. HttpClient
simplifies the default ergonomics (you don't need to map to JSON anymore) and now supports typed return values and interceptors.rxjs-compat
.$ ng generate webWorker my-worker
/deep/
with ::ng-deep
in your styles, both are deprecated but using ::ng-deep
is preferred until the shadow-piercing descendant combinator is removed from browsers and tools completely.$ npm update -g typescript
ViewChild
or ContentChild
, you must now specify that change detection should run before results are set (check Breaking change in ViewChild and ContentChild chapter above).$ ng update @angular/material
If you're trying to upgrade from a different version than Angular version 7 you can use the official Angular upgrade guide for instructions on how to proceed.
Not counting the Ivy, additions to Angular version 8 aren’t large and they aren’t critical for the most of Angular applications. Having in mind that there aren’t any large breaking changes, you should update your application and in most cases it will run without any changes. With the addition to enable differential loading, you’ll get noticeable performance gains. More importantly, upgrading to Angular 8 will ensure that your application is ready for full featured Ivy. Even though Ivy is currently only an opt-in preview in Angular version 8, this is a good time to start checking for Ivy compatibility and make changes (if needed) before Ivy in Angular 9 or 10 becomes default and the current Angular View Engine is deprecated.
If you have any further questions or comments, please contact us.