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

Angular Revisited: Tree-shakable Components and Optional NgModules

Angular Revisited: Tree-shakable Components and Optional NgModules

NgModule is arguably one of the most confusing Angular concepts. Using tree-shakable components and dependencies, we will need Angular modules less often or not at all. Tree-shakable components are not available yet, but we can use Single Component Angular Modules to ease the migration path.

Presented at:
- ngVikings conference, May 2019
- Angular In Depth conference, June 2019

Recording of talk from the ngVikings 2019 conference:
https://youtu.be/DA3efofhpq4

Article:
https://dev.to/this-is-angular/angular-revisited-tree-shakable-components-and-optional-ngmodules-36d2

More Decks by Lars Gyrup Brink Nielsen

Other Decks in Programming

Transcript

  1. Angular Revisited
    Tree-shakable Components and Optional NgModules

    View full-size slide

  2. I think NgModule is something that if we didn’t have to,
    we wouldn’t introduce. Back in the day, there was a real
    need for it. With Ivy and other changes to Angular over
    the years, we are working towards making those
    optional.
    — Igor Minar at AngularConnect 2018

    View full-size slide

  3. The way NgModule works tends to be confusing to new
    Angular developers. Even if we weren’t to rip it out
    completely, we would change how it works and simplify
    things.
    — Alex Rickabaugh at AngularConnect 2018

    View full-size slide

  4. Ivy out-of-the-box
    • Faster builds
    • Better debugging
    • Smaller bundle size
    • Instruction set similar to Incremental DOM

    View full-size slide

  5. Ivy is well-suited for
    • Micro front-ends
    • Angular Elements
    • Web apps where Angular is not in control
    of the entire document

    View full-size slide

  6. Zoneless change detection with Ivy
    1. Change local UI state.
    2. Mark component for check.
    3. Schedule change detection
    cycle.
    @Component({
    selector: 'zippy',
    template: `{{title}}
    `,
    })
    export class ZippyComponent {
    @Input() title: string;
    isExpanded = false;
    onToggle() {
    this.isExpanded = !this.isExpanded;
    markDirty(this);
    }
    }
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  7. We use Angular modules to
    • Configure injectors for runtime resolving of
    dependencies
    • Link declarables when compiling component
    templates
    • Bootstrap components as entry points to our
    applications
    • Lazy-load application features

    View full-size slide

  8. Tree-shakable components are
    • Entry components but tree-shakable by the
    build process
    • Self-contained and independent from Angular
    modules
    • The smallest compilation and code splitting
    unit for Angular

    View full-size slide

  9. import { Component, Input, ɵmarkDirty as markDirty } from '@angular/core';
    import { ButtonDirective } from './button.directive';
    @Component({
    deps: [
    ButtonDirective,
    ],
    selector: 'zippy',
    template: `{{title}}
    `,
    })
    export class ZippyComponent {
    @Input() title: string;
    isExpanded = false;
    onToggle() {
    this.isExpanded = !this.isExpanded;
    markDirty(this);
    }
    }
    Tree-shakable
    components with
    proposed component API
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  10. import { Component } from '@angular/core';
    import { CapitalizePipe } from './capitalize.pipe';
    import { ZippyComponent } from './zippy.component';
    @Component({
    deps: [
    CapitalizePipe,
    ZippyComponent,
    ],
    template: `{{title | capitalize}}`,
    })
    export class AppComponent {
    title = 'proposed component API';
    }
    Tree-shakable
    components with
    proposed component API
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  11. Single component Angular modules
    • Declare and export only one component
    • Import only the declarables used by their specific
    component
    • Are useful for isolated component tests
    • Can be placed in the same file as their component
    • The abbreviated form is SCAM
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  12. import { Component, Input, NgModule } from '@angular/core';
    import { ButtonModule } from './button.directive';
    @Component({
    selector: 'zippy',
    template: `{{title}}
    `,
    })
    export class ZippyComponent {
    @Input() title: string;
    isExpanded = false;
    onToggle() {
    this.isExpanded = !this.isExpanded;
    }
    }
    @NgModule({
    declarations: [ZippyComponent],
    exports: [ZippyComponent],
    imports: [ButtonModule],
    })
    export class ZippyModule {}
    Faux tree-shakable
    components with
    SCAMs
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  13. import { Component, NgModule } from '@angular/core';
    import { CapitalizeModule } from './capitalize.pipe';
    import { ZippyModule } from './zippy.component';
    @Component({
    template: `{{title | capitalize}}`,
    })
    export class AppComponent {
    title = 'single component angular modules’;
    }
    @NgModule({
    declarations: [AppComponent],
    imports: [
    CapitalizeModule,
    ZippyModule,
    ],
    })
    export class AppModule {}
    Faux tree-shakable
    components with
    SCAMs
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  14. Component render modules
    • Exclusively declare dependencies, no Angular module
    imports
    • Declare all the declarables used by their specific
    component and additionally the component itself
    • Can have duplicate declarations between Angular
    modules
    • Only work because of a timing issue
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  15. import { Component, Input, NgModule, ɵmarkDirty as markDirty }
    from '@angular/core';
    import { ButtonDirective } from './button.directive';
    @Component({ selector: 'zippy',
    template: `{{title}}
    `,
    })
    export class ZippyComponent {
    @Input() title: string;
    isExpanded = false;
    onToggle() {
    this.isExpanded = !this.isExpanded;
    markDirty(this);
    }
    }
    @NgModule({ declarations: [
    ZippyComponent,
    ButtonDirective,
    ] })
    export class ZippyRenderModule {}
    Component render
    modules
    (Ivy preview with JIT)
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  16. import { Component, NgModule } from '@angular/core';
    import { CapitalizePipe } from './capitalize.pipe';
    import { ZippyComponent } from './zippy.component';
    @Component({
    template: `{{title | capitalize}}`,
    })
    export class AppComponent {
    title = 'component render modules’;
    }
    @NgModule({
    declarations: [
    AppComponent,
    CapitalizePipe,
    ZippyComponent,
    ],
    })
    export class AppRenderModule {}
    Component render
    modules
    (Ivy preview with JIT)
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  17. Feature render modules
    • Every component exports a nested declarable array
    • A single feature render module per bundle
    • Angular will flatten the declarable arrays and remove
    duplicates
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  18. import { Component, Input, ɵmarkDirty as markDirty } from '@angular/core';
    import { ButtonDirective } from './button.directive';
    @Component({
    selector: 'zippy',
    template: `{{title}}
    `,
    })
    export class ZippyComponent {
    @Input() title: string;
    isExpanded = false;
    onToggle() {
    this.isExpanded = !this.isExpanded;
    markDirty(this);
    }
    }
    export const zippyDeps = [
    ZippyComponent,
    ButtonDirective,
    ];
    Feature render
    modules
    (Ivy preview with AOT)
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  19. import { Component, NgModule } from '@angular/core';
    import { CapitalizePipe } from './capitalize.pipe';
    import { zippyDeps } from './zippy.component';
    @Component({
    template: `{{title | capitalize}}`,
    })
    export class AppComponent {
    title = 'feature render modules’;
    }
    @NgModule({
    declarations: [
    AppComponent,
    CapitalizePipe,
    zippyDeps,
    ],
    })
    export class AppRenderModule {}
    Feature render
    modules
    (Ivy preview with AOT)
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  20. We use Angular modules to
    • Configure injectors for runtime resolving of
    dependencies
    • Link declarables when compiling component
    templates
    • Bootstrap components as entry points to our
    applications
    • Lazy-load application features

    View full-size slide

  21. Bootstrapping tree-shakable components
    Unlike View Engine:
    • No NgZone
    • No default change detection
    • No application initialisers
    • No application initialisation
    status
    • No application bootstrap
    hook
    import { ɵrenderComponent as renderComponent }
    from '@angular/core';
    import { AppComponent } from './app.component';
    renderComponent(AppComponent);
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  22. We use Angular modules to
    • Configure injectors for runtime resolving of
    dependencies
    • Link declarables when compiling component
    templates
    • Bootstrap components as entry points to our
    applications
    • Lazy-load application features

    View full-size slide

  23. Lazy-loading tree-shakable components
    1. Dynamically import the
    component file
    2. Extract the component from
    the file
    3. Bootstrap the component
    import { ɵrenderComponent as renderComponent }
    from '@angular/core';
    import('./my-ivy.component').then(({ MyIvyComponent }) =>
    renderComponent(MyIvyComponent);
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  24. This talk is possible thanks to
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES
    Alexey Zuev Joost Koehoorn Max Koretskyi
    Morten Kirstein Wassim Chegham Zejiang Yu

    View full-size slide

  25. Tree-shakable components with Ivy
    • Get rid of all Angular modules
    • Bootstrap using renderComponent
    • Schedule change detection using markDirty when local
    UI state changes
    • Lazy-load application features using dynamic import
    and renderComponent
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES

    View full-size slide

  26. Techniques for getting started today
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES
    View Engine Ivy preview with JIT Ivy preview with AOT
    SCAMs ✓ ✓ ✓
    Component render modules ✓
    Feature render modules ✓ ✓
    Proposed component API

    View full-size slide

  27. Get in touch
    ANGULAR REVISITED: TREE-SHAKABLE COMPONENTS AND OPTIONAL NGMODULES
    bit.do/angular-revisited-slides
    bit.do/angular-revisited
    github.com/LayZeeDK/angular-revisited
    github.com/LayZeeDK
    twitter.com/LayZeeDK
    www.linkedin.com/in/larsgbn/
    [email protected]

    View full-size slide