Ivy Rendering Engine

May 12, 2020 by Christopher Sherman

Ivy is the compiler and rendering pipeline that converts Angular templates into code the browser renders. Ivy introduces several improvements over ViewEngine, the previous compiler and rendering pipeline. Below, I highlight the most noteworthy advances.

Bundle size

According to Angular core team member Vikram Subramanian (2019), reduced bundle size was the main motivation for developing Ivy. Ivy removes from our final JavaScript bundle the Angular template features we aren’t using (Fluin, 2020). Ivy accomplishes this by optimizing its DOM-generation code so tree-shaking tools can more effectively perform static analysis of our code (Google I/O, 2018). Static analysis examines code without actually running it.

ViewEngine included numerous conditionals in its DOM-generation code to accommodate the possibility an Angular template made use of features like directives, event listeners, and pipes (Google I/O, 2018). For each component, ViewEngine created an instance of a component data structure at buildtime, and Angular used an interpreter at runtime to convert this data structure into a DOM element (Google I/O, 2018). The conditional statements within the data structure’s generate method didn’t allow the tree-shaking tool to remove unused template features from our code, because it was unclear to the tree-shaker whether our templates would ultimately use the conditionally-added features.

Ivy replaces the component data structure with instruction sets (Google I/O, 2018). These instructions specify the set of steps necessary to generate a component’s DOM. If the set of instructions do not include function calls for directive, event listener, pipe features, etc. the tree-shaker does not include these features in the final JavaScript bundle. If we need to optimize a particular portion of our application, we can separate it into its own module (thereby creating its own bundle if we lazy load the module) and exclude template features until we reach our desired bundle size.

Ivy can tree shake (Subramanian, 2019):

  • Dependency injection
  • NgTemplate
  • NgContainer
  • View and content queries
  • Animations
  • Pipes
  • i18n
  • Core framework services

With Ivy, small applications see significant reductions in bundle sizes because they tend to use a fraction of Angular’s features. Ivy reduces a 36 Kb “Hello World” application to 2.7 Kb (Google I/O, 2018). Large applications see a significant reduction in the size of factory functions (Fluin, 2020). Angular uses factories to provide dependency injection (Angular, Version 9.1.7-local+sha.790af88288-a). Medium-sized applications will not see much reduction in bundle sizes. These applications see less benefit from tree-shaking and do not have enough components to leverage smaller factories in a significant way (Fluin, 2020).

Environment parity

Due to the improved compilation performance of Ivy, Angular now enables ahead-of-time compilation (AOT) by default in dev-mode (Fluin, 2020). This enables enhanced compile-time checking, allowing us to catch template errors during the development cycle. Angular previously waited to perform some checks until we built our code for production because the checks took significant time to run. Now we can enjoy the benefits of AOT when we run ng serve.

Like the ng serve development environment, tests in Angular 9 take advantage of AOT. This allows us to catch errors sooner in the development process and minimize differences between our testing and production environments (Nielsen, 2020).

Build optimization

More efficiently rebuilding components contributes significantly to Ivy’s improved compilation times. When compiling components, ViewEngine included the build of each component’s child components (Google I/O, 2018). When we made changes to a child component during development, seeing these changes required rebuilding all of the child’s parent components. With Ivy, each component exists as its own “locality”, meaning Angular recompiles only the component we changed (Google I/O, 2018). As a result, we pay for the compile time of a single component rather than the entire component hierarchy.

Using Ivy reduces the compilation penalty of taking dependencies on third-party components. With ViewEngine, our project’s compiler was responsible for compiling third-party libraries such as Angular Material. With Ivy, library owners can pre-compile their code, speeding up the compile time of projects that take dependencies on their libraries (Subramiamian, 2019).

Another benefit of Ivy is we no longer need to specify entryComponents for components not referenced within a template (Fluin, 2020). Ivy discovers these components and registers them for us.

Ivy’s build optimizations extend to testing as well. In the past, Angular’s TestBed environment recompiled components between tests regardless of whether we made any changes to the components (Fluin, 2020). This meant between each test step (i.e. it declaration), TestBed recompiled all components used in the test step even if we’d already compiled these components previously (Nielsen, 2020). Ivy makes TestBed more efficient by recompiling only components we manually override (Fluin, 2020).

Also shipping with Angular 9 is the TestBed.inject function. Replacing TestBed.get, the inject function enables tests to use strongly typed injectables (Nielsen, 2020). What this means in practice is when we inject a service in a test, we get compile-time type checking as well as Intellisense to help us write our tests in a way that adheres to the service’s API.

Type checking

Ivy improves Angular templates' ability to validate the types we apply to component, directive, and pipe bindings (Angular, Version 9.1.7-local+sha.790af88288-b). This includes inferring the correct type when components and directives use generics (Angular, Version 9.1.7-local+sha.790af88288-b). Ivy can infer context types as well. One example of context type inference is how Ivy validates that the object instantiated by NgFor has the properties we use within the NgFor’s template (Angular, Version 9.1.7-local+sha.790af88288-b).

Debugging

Ivy improves the stack trace detail for debugging a exception. The error message includes more suggestions for rectifying the problem and the stack trace points more specifically to the line of code causing the error (Fluin, 2020).

Another improvement Ivy adds to the debugging experience is the addition of the ng namespace variable to the window object. This object allows us to access instances of our components and directives, manually call component methods, update component state, and see the results of our changes by manually triggering change detection (Fluin, 2020).

Precedence of styling bindings

Ivy introduces styling precedence rules to ensure a predictable application of styles when multiple definitions conflict. The most specific styles have highest precedence (Fluin, 2020). We can also access CSS custom properties, sometimes referred to as CSS variables, via Angular bindings (Fluin, 2020).

Internationalization (i18n)

Ivy moves i18n substitutions later in the build process, resulting in a 10x improvement in the time required to perform substitution (Fluin, 2020).

API improvements

Ivy introduces the ng.markDirty method, allowing us to take fine-grain control of change detection in our components. Rather than relying on Zone.js to patch the browser’s asynchronous methods and trigger change detection whenever the browser fires an asynchronous method, we can manually call markDirty to have Angular schedule a change detection cycle (Ryan, 2019). This pattern is useful when we have asynchronous events continually firing but we’re only interested in a subset of these events (e.g. WebSocket connection). Rather than having Zone.js trigger change detection on every WebSocket message, we can filter the messages and trigger change detection only on messages that result in an update to our templates. The reduced change detection churn enables us to handle larger streams of data.

The Ivy API also enables us to write test harnesses for components. Testing components typically relies on using implementation details like CSS selectors to find elements and trigger events (Nielsen, 2020). If the implementation of the component changes, our tests break. In Angular 9, we can use a component harness to provide a public API for testing a component. This feature is especially useful for library authors. Angular Material has already written harnesses for testing the public API of its components, so instead of querying for a select box’s -mat-option elements and trigger a click, we can now query by MatSelect and trigger a click with mySelectBox.clickOptions({text: 'Option 1'}) (Nielsen, 2020). This gives component authors flexibility to change their implementation without causing breaking changes.

Wrap up

Angular’s new compiler and rendering pipeline, Ivy, allows us to build Angular applications more efficiently and enables our applications to run faster. We get quicker development cycles from improved compiler speed. We have better parity between development, test, and production environments. Our applications download and bootstrap more quickly due to reduced bundle size. And possibly the best feature of all: most applications generated with the Angular CLI will seamlessly upgrade to use Ivy without manual intervention (Angular, Version 9.1.7-local+sha.790af88288-c).

Sources

Angular. (Version 9.1.7-local+sha.790af88288-a). Dependency Injection Providers. Google. https://angular.io/guide/dependency-injection-providers#factory-providers

Angular. (9.1.7-local+sha.790af88288-b) Template type checking. Google. https://angular.io/guide/template-typecheck

Angular. (Version 9.1.7-local+sha.790af88288-c). Ivy compatibility guide. Google. https://angular.io/guide/ivy-compatibility#ivy-compatibility-guide

Fluin, S. (2020, February 9). Version 9 of Angular Now Available–Project Ivy has arrived! Angular. https://blog.angular.io/version-9-of-angular-now-available-project-ivy-has-arrived-23c97b63cfa3

Google I/O . (2018, May 9). What’s new in Angular [Video]. Youtube. What’s new in Angular (Google I/O ‘18)

Nielsen, L. (2020, January 27). Next-level testing in Angular Ivy version 9. inDepth.dev. https://indepth.dev/next-level-testing-in-angular-ivy-version-9/

Ryan, M. (2019, September 27). Building with Ivy: rethinking reactive Angular [Video]. AngularConnect 2019. Building with Ivy: rethinking reactive Angular | Mike Ryan | #AngularConnect 2019

Subramanian , V. (2019, February 28). Overview of Ivy, Angular’s new renderer [Video]. ng-India 2019. Overview of Ivy, Angular’s new renderer - Vikram Subramanian | ng-India 2019

Angular