Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Recap Theory of Ivy

Recap Theory of Ivy

Recap The Theory of Angular Ivy from AngularConnect 2018

Siwat Kaolueng

November 28, 2018
Tweet

More Decks by Siwat Kaolueng

Other Decks in Programming

Transcript

  1. Siwat Kaolueng - Angular & Web & Front-end Enthusiast -

    Freelancer - Angular Thailand Organizer
  2. Angular has two parts - Angular Renderer - Code that

    we generate when your ngc AOT compiler against your template - NgFactory File - Angular code import from core, browser support compile at runtime - Runtime - Angular gets a lot of feedback from companies, developer relations and so on. - They want Angular to be simpler, easier to pick up and understand, build faster, smaller bundles and advance features such as HOC. - Current rendering architecture (View Engine) is limited. - Ivy is new back-end for compiler with new runtime. - Backward Compatible with View Engine 4.0
  3. Ivy simplify internal Angular - Compilation - How we compile

    the app - Deployment - How we package bundle and shipping libraries on npm - Dependency - Link to together and depend on each other at runtime
  4. Goals Optimizability → pay as you go both you template

    code and runtime code Incrementality → faster compilation, simpler libraries reduce complex meta data on NPM Flexibility → more powerful foundation new features
  5. 2.0 Template Compiler ( 2 years ago) Imperative List of

    Operation NgFactory per @Component, @NgModule Templates → DOM calls Generate fast, optimized code
  6. Compiled with Angular 2.0 const parentRenderNode:any = this.renderer.createViewRoot(this.parentElement); this._text_0 =

    this.renderer.createText(parentRenderNode,'\n ',null); this._el_1 = ng.createRenderElement(this.renderer,parentRenderNode,'div',ng.EMPTY_INLINE_ARRAY,null); this._text_2 = this.renderer.createText(this._el_1,'\n ',null); this._el_3 = ng.createRenderElement(this.renderer,this._el_1,'span',ng.EMPTY_INLINE_ARRAY,null); this._text_4 = this.renderer.createText(this._el_3,'',null); this._text_5 = this.renderer.createText(this._el_1,'\n ',null); this._anchor_6 = this.renderer.createTemplateAnchor(this._el_1,null); this._vc_6 = new ng.ViewContainer(6,1,this,this._anchor_6); this._TemplateRef_6_5 = new ng.TemplateRef_(this,6,this._anchor_6); this._NgIf_6_6 = new ng.Wrapper_NgIf(this._vc_6.vcRef,this._TemplateRef_6_5); this._text_7 = this.renderer.createText(this._el_1,'\n',null); this._text_8 = this.renderer.createText(parentRenderNode,'\n',null); From research → One of the fastest way to create dom structure in the browser Get feedback → Too verbose, Too many bytes, Too much overhead to bundles Different Approach to represent template with less overhead -> View Engine 4.0
  7. 4.0+ View Engine Instead of rendering instructions, generate a data

    structure Data structure is interpreted at runtime Generate small, optimal structures (optimize template size and not to sacrifice performance during rendering)
  8. Compile with Angular 7.0 function View_RootCmp_0(_l) { return ng.ɵvid(0, [

    (_l()(), ng.ɵeld(0, 0, null, null, 4, "div", [], null, null, null, null, null)), (_l()(), ng.ɵeld(1, 0, null, null, 1, "span", [], null, null, null, null, null)), (_l()(), ng.ɵted(2, null, ["", ""])), (_l()(), ng.ɵand(16777216, null, null, 1, null, View_RootCmp_1)), ng.ɵdid(4, 16384, null, 0, ng.NgIf, [ng.ViewContainerRef, ng.TemplateRef], {ngIf: [0, "ngIf"]}, null) ], ...); } Smaller, harder to understand (magic number & null) General Idea - div, span , nextline text node, child component, ngIf control child component
  9. Ivy replace View Engine while remaining backwards compatible Hundred of

    hundreds of Angular apps at Google process of testing Ivy (thousands tests each) not only internal test suite
  10. Annotations → Static Fields @Component → ngComponentDef @Directive → ngDirectiveDef

    @Injectable → ngInjectableDef Used to be produced in separate NgFactory files Move into component and directives or classes itself
  11. @Component({ selector: 'root-cmp', template: ` <div> <span>{{title}}</span> <child-cmp *ngIf="show"></child-cmp> </div>`,

    }) export class RootCmp { … } export class RootCmp { … } RootCmp.ngComponentDef = ng.ɵdefineComponent({ type: RootCmp, selectors: [["test-cmp"]], consts: 4, vars: 2, factory: function RootCmp_Factory(t) { return new (t || RootCmp)(); }, directives: [ChildCmp, ng.NgIf], template: function RootCmp_Template(rf, ctx) { … }, }); Your code Compiled by Ivy Static Field Contain metadata - tell ivy about information about component such as selector, two directives child component, ngIf Template Function - rendering template DOM & Change Detection
  12. Template Function <div> <span>{{title}}</span> <child-cmp *ngIf="show"></child-cmp> </div> function RootCmp_Template(rf, ctx)

    { if (rf & 1) { ng.ɵelementStart(0, "div"); ng.ɵelementStart(1, "span"); ng.ɵtext(2); ng.ɵelementEnd(); ng.ɵtemplate(3, RootCmp_child_cmp_Template_3, 1, 0, null, [1, "ngIf"]); ng.ɵelementEnd(); } if (rf & 2) { ng.ɵtextBinding(2, ng.ɵinterpolation1("", ctx.title, "")); ng.ɵelementProperty(3, "ngIf", ng.ɵbind(ctx.show)); } } Imperative calls like Template Compiler 2.0 Create div, span, child template with ngIf First if block render the first time insert into DOM Second block run change detection, running two binding interpolation and condition for ngIf
  13. Ivy templates use a new Instruction Set instead of single

    template interpreter at runtime in View Engine
  14. Ivy Instruction Set DOM creation Data binding Change detection i18n

    Queries Dependency Injection Styling Containers Templates Content Projection Pipes SVG
  15. Example Service @Injectable() export class AuthGuard { constructor( router: Router,

    http: HttpClient, @Inject(CONFIG) config: any, ) { … } } - Auth Guard check when navigate in - Injecting Router, HTTPClient, config object - Check permission in config for current url → HttpClient calls for user permission → router navigate away if no permission - In Ivy @Injectable -> ngInjectableDef
  16. Injectable() xport class AuthGuard { constructor( router: Router, http: HttpClient,

    @Inject(CONFIG) config: any, AuthGuard.ngInjectableDef = ng.ɵdefineInjectable({ factory: function AuthGuard_Factory() { return new AuthGuard( ng.ɵinject(Router), ng.ɵinject(HttpClient), ng.ɵinject(CONFIG), - ngInjectableDef has factory function. Factory function is to create the instance. - For every dependency needing to be injected. Factory function call ivy inject instruction. Give token and get back instance of the dependency - Once those instances have been created factory function calls constructor. - If no dependency, no inject instruction
  17. Optimizability Don’t pay (in bytes) for unused framework features Achieved

    via tree-shaking Ivy let us leverage a JS tools that optimize code especially Tree Shaking. Not use feature in app, don’t pay the cost of bytes at runtime
  18. Tree Shaking aka dead code elimination aka live code inclusion

    Optimizer looks to prove a certain code is not used at runtime, and remove the code from bundle and make it smaller. How do I write code that I do not use?
  19. Why tree shake? Application code is typically always used Libraries

    often include code you don’t use Including Angular! import from @angular/material. ES6 import all by default Tree-shaker does understand code enough you use only button. The rest is thrown away.
  20. How to tree shake Rollup - ES6 bundler removes not

    referencing code Webpack 2 - inspired by Rollup design Uglify (deprecated) → Terser (CLI)* Build-Optimizer(Angular) - know how to annotate better to tell Uglify Closure Compiler(JS Compiler) - smallest bundle, code specific way *https://github.com/terser-js/terser
  21. How to tree shake Rollup - ES6 bundler removes not

    referencing code Webpack 2 - inspired by Rollup design Uglify (deprecated) → Terser (CLI)* Build-Optimizer(Angular) - know how to annotate better to tell Uglify Closure Compiler(JS Compiler) - smallest bundle, code specific way *https://github.com/terser-js/terser
  22. Ivy is designed for tree shaking Broke runtime up in

    big interpreter into smaller functions, separated instructions get compiled in your template Ex. Not use i18n, content projection or SVG no instruction in compiled template
  23. Ivy can tree shake Dependency Injection <ng-template> and <ng-container> View

    and Content Queries Animations Pipes i18n Core framework services
  24. 4.0+: View Engine Compiler generated data structure and runtime, runtime

    traverse and interpreted them. The interpreter has to handle whatever template. It doesn’t know ahead of time you use content projection, i18n or SVG. While optimizing interpreter small and optimal, the tree-shaker could not remove part of it. Interprets generated NgFactories at runtime Must support every Angular feature Impossible to tree shake or split up
  25. Hello World Good gauge of tree-shakeability Representative of some use

    cases (e.g. NgElement) My app is not hello world but you definitely pay what you use. Angular Element is not complex as an app, Ivy shakes them down to where they work.
  26. Optimizability Ivy is optimizable today (Optimizable with current suite JS

    Tool) Optimizers are a work in progress (new tools is invented everyday) Ivy is ready for future optimizers too (Ex. Closure eventually commonly uses, Ivy will be ready.)
  27. Incrementality When an Ivy application depends on @angular/material, Material is

    already compiled when it’s installed Template Compiler & View Engine 4.0+ used global compilation (rebuild dependency, libraries ) - typescript, ngc It’s fairly difficult to get that. To do it, ivy implemented a rule call “locality”
  28. Locality Components/directives/etc. depend on each other via public API only

    Compiling them requires only local information Safe to ship code in NPM Can compile them without needing to know too much about those dependencies using DTS file without extra medata about code. Ivy add information to DTS files for public API like selector and purely rely on TypeScript.
  29. @Input @Input('property') field: string; \ Public name Private name (Implementation

    Detail) Component can change the field without breaking because property name is the same.
  30. @Input Ex. Binding and directly using private API, it’s still

    work. When component or directive changes, we have to regenerate this code to assume “field” changed. We don’t know when they publish a new patch. Angular today relies on global compilation. <div directive [property]="value"> function updateBindings(directive: any, value: any) { directive.field = value; } Private name
  31. @Input Ivy property binding generates element property instruction taking input

    and new value, looks up directive or component then writing value out. <div directive [property]="value"> ng.elementProperty(1, "property", ng.bind(ctx.value));
  32. Locality in View Engine function View_RootCmp_0(_l) { return ng.ɵvid(0, [

    (_l()(), ng.ɵeld(0, 0, null, null, 4, "div", [], null, null, null, null, null)), (_l()(), ng.ɵeld(1, 0, null, null, 1, "span", [], null, null, null, null, null)), (_l()(), ng.ɵted(2, null, ["", ""])), (_l()(), ng.ɵand(16777216, null, null, 1, null, View_RootCmp_1)), ng.ɵdid(4, 16384, null, 0, ng.NgIf, [ng.ViewContainerRef, ng.TemplateRef], {ngIf: [0, "ngIf"]}, null) ], ...); } ViewContainerRef and TemplateRef are constructor parameters for NgIf to be injected - private API of ngIf. Angular Team updates ngIf, inject something, it might break code we generate and that’s why we generate code at the end of app bundle.
  33. Locality in Ivy ViewContainerRef and TemplateRef are constructor parameters for

    NgIf to be injected - private API of ngIf. Angular Team updates ngIf, inject something, it might break code we generate and that’s why we generate code at the end of app bundle. NgIf.ngDirectiveDef = i0.ɵdefineDirective({ factory: function NgIf_Factory(t) { return new NgIf( i0.ɵinject(ViewContainerRef), i0.ɵinject(TemplateRef) ); }, ... }); Constructor parameters go into factory function. This code is safe to ship to NPM. ngIf has information encoded on it. Just call factory function.
  34. Incrementality Ivy provides a “stable API” that allows shipping AOT

    compiled code to NPM ng build only builds your app No big monolithic global compilation → build libs & apps separately Not only build faster, but also simplify how libraries packaged using public API no metadata
  35. Flexibility Ivy is a flexible foundation for Angular going forward

    Not focused on new features (for now) Never been about delivering exciting new features on the day that we ship ivy, instead, it’s about make Angular better, faster compiles, small bundles, better debuggability. However, during undertaking big effort to make our design give us a foundation for building cool features users wanted - good technical direction. Ivy Instruction sets make us very extensible - New instructions, no remove instructions - existing apps still work.
  36. Ivy new features Better JIT/AOT interop (hybrid app - AOT

    with JIT such as $compile Angular.js, cost of shipping compiler to browser) No .ngfactory complications (still generate apps because still import, but shims, in the future, bootstrapping API simpler) Lazy loading without the Router (Now lazy-load Angular only CLI) Dynamic import & deployment
  37. Potential future ideas Hand written templates (write ivy instruction sets!!!!!!!!!!!)

    Ecosystem on top of Ivy (empower the library from ivy low-level APIs) Higher order components (function wrap component, function adds behavior or does something else)
  38. Flexibility Ivy is a flexible foundation for Angular in the

    future Not focused on new features (for now) Once it’s achieved, long list of ideas to investigate
  39. Summary Significant refactoring Simplify Angular internal complexity, remove need of

    NgFactory file, simplify bootstrapping, improve lazy-loading, get rid of user stumbling block Instruction set makes templates optimizable, and make framework itself tree-shakeable. Global to incremental compilation, build faster Reduce requirement on libraries