Slide 1

Slide 1 text

The Hidden Docs In Angular 谢亚东(执衡) Senior Front-end Engineer Alibaba

Slide 2

Slide 2 text

谢亚东(执衡) Senior Front-end Engineer Alibaba

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Angular Evolution 2.0 4.0 5.0 6.0 7.0 8.0 9.0 2016 Sep 2019 Nov

Slide 5

Slide 5 text

import { Component } from '@angular/core'; @Component({ selector: 'app-root', template: `

Hello {{name}}

`, }) export class AppComponent { name = 'Angular'; } 2.0 4.0 6.0 defineComponent({ type: AppComponent, selectors: [ ["app-root"] ], decls: 3, vars: 1, template: function AppComponent_Template(rf, ctx) { if (rf & 1) { elementStart(0, "div"); elementStart(1, "h2"); text(2); elementEnd(); elementEnd(); } if (rf & 2) { advance(2); textInterpolate1("Hello ", ctx.name, ""); } }, encapsulation: 2 }); 9.0.0-rc.3 Code After Compiler

Slide 6

Slide 6 text

4.0 1170 KB 9.0.0-rc.3 gzip 30 KB Bundle Size After Compiler 8.0 133 KB Differential Loading 9.0.0-rc.3 94.8 KB Ivy 5.0 152 KB

Slide 7

Slide 7 text

Broccoli Webpack Bazel 2 3 4 Command-Line Interface @angular/cli +

Slide 8

Slide 8 text

Stable & Reliable

Slide 9

Slide 9 text

DATE STABLE RELEASE COMPATIBILITY October/November 2019 9.0.0 ^8.0.0 May 2020 10.0.0 ^9.0.0 Release schedule & Semantic Versioning ng update & Update Guide ng update @angular/cli @angular/core

Slide 10

Slide 10 text

The Hidden Docs in Angular

Slide 11

Slide 11 text

The Hidden Docs in Angular

Slide 12

Slide 12 text

Change Detection The Hidden Docs in

Slide 13

Slide 13 text

@Component({ selector: "hello", template: `
{{ count }}
add minus ` }) export class HelloComponent { count = 0; add() { this.count += 1; } minus() { this.count -= 1; } } Counter Change Detection DEMO

Slide 14

Slide 14 text

Change Detection React Change to state cause change detection setState or hooks Angular ?

Slide 15

Slide 15 text

When to Trigger Change Detection? • Events: all browser events (click, mouseover, keyup, etc.) • Timers: setTimeout() and setInterval() • XHR: Ajax requests Browser Async API

Slide 16

Slide 16 text

When to Trigger Change Detection? Monkey Patch zone.js • Events: all browser events (click, mouseover, keyup, etc.) • Timers: setTimeout() and setInterval() • XHR: Ajax requests Browser Async API DEMO

Slide 17

Slide 17 text

this.applicationRef.tick() this._zone.onMicrotaskEmpty.subscribe( { next: () => { this._zone.run(() => { this.tick(); }); } }); _loadComponent(componentRef) { this.tick(); } Explore the Source Code ApplicationRef Demo with zone.js

Slide 18

Slide 18 text

for (let view of this._views) { view.detectChanges(); } this.applicationRef.tick() ChangeDetectionRef CD CD CD CD CD CD CD CD CD CD CD this.cdr.detectChanges() Explore the Source Code

Slide 19

Slide 19 text

for (let view of this._views) { view.detectChanges(); } this.applicationRef.tick() ChangeDetectionRef CD CD CD CD CD CD CD CD CD CD CD Demo with ChangeDetectionRef Explore the Source Code

Slide 20

Slide 20 text

detach() markForCheck() detectChanges() OnPush ChangeDetectionRef Explore the Source Code

Slide 21

Slide 21 text

ViewModel View Detection Sequence Why is it important? DEMO

Slide 22

Slide 22 text

Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. DEMO

Slide 23

Slide 23 text

1. update bound properties for all child components 2. call OnChanges, OnInit, DoCheck and AfterContentInit lifecycle hooks on all child components 3. update DOM for the current component 4. run change detection for a child component 5. call ngAfterViewInit lifecycle hook for all child components Detection Sequence Sequence is slightly different under Ivy engine

Slide 24

Slide 24 text

One Way Data Flow Unidirectional Data Flow VS

Slide 25

Slide 25 text

Summary 1. When 2. Source Code 3. Detection Sequence zone.js + tick ApplicationRef + ChangeDetectionRef ViewModel —> View & Unidirectional Data Flow

Slide 26

Slide 26 text

Component | Directive 1. Selector 2. @Input 3. Content Projection 4. Dynamic Component The Hidden Docs in

Slide 27

Slide 27 text

Selector

Slide 28

Slide 28 text

Selector const _SELECTOR_REGEXP = new RegExp( '(\\:not\\()|' + //":not(" '([-\\w]+)|' + // "tag" '(?:\\.([-\\w]+))|' + // ".class" // "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range '(?:\\[([-.\\w*]+)(?:=([\"\']?)([^\\]\"\']*)\\5)?\\])|' + // "[name]", "[name=value]", // "[name="value"]", // "[name='value']" '(\\))|' + // ")" '(\\s*,\\s*)', // "," 'g'); export interface Component extends Directive Explore the Source Code 1. :not() 2. Tag 3. Attribute Same Selector

Slide 29

Slide 29 text

Attribute Selector in Component VS 1. Supporting Original Attributes {{name}} .parent > .child{ height: 200px; background: gray; } 2. Keep DOM structure

Slide 30

Slide 30 text

Tag Selector in Directive Lighthouse Scoring import { Directive, HostBinding } from '@angular/core'; @Directive({ selector: 'a[target="_blank"]:not([rel="noopener"])' }) export class SafeLinkDirective { @HostBinding('style.color') color = 'red'; @HostBinding('rel') rel = 'noopener'; }

Slide 31

Slide 31 text

@Input

Slide 32

Slide 32 text

typeof @Input data Input Correction this.data === true

Slide 33

Slide 33 text

@Input as Attribute @Input() set ngxOpen(value) { this._open = this.coerceBooleanProperty(value); } get ngxOpen() { return this._open; } coerceBooleanProperty(value: any): boolean { return value != null && `${value}` !== "false"; } @InputBoolean() ngxOpen = false; Decorators

Slide 34

Slide 34 text

@Input with *
...
Loading...
@Input() set ngIfElse(templateRef: TemplateRef|null) { assertTemplate('ngIfElse', templateRef); this._elseTemplateRef = templateRef; this._elseViewRef = null; // clear previous view if any. this._updateView(); } DEMO

Slide 35

Slide 35 text

Content Projection

Slide 36

Slide 36 text

Content Projection Multi-Slot 1. Tag Selector 2. Attribute Selector 3. Not Selector 4. Default Selector 5. ngProjectAs

Slide 37

Slide 37 text

Content Projection Component Interacting ContentChildren 1. ChangeDetection Error 2. NgTemplateOutlet

Slide 38

Slide 38 text

Content Projection Component Interacting Dependency Injection 1. NgTemplateOutlet 2. Get Root Component

Slide 39

Slide 39 text

Content Projection Content Empty Detection

Slide 40

Slide 40 text

Content Projection Content Empty Detection ContentObserver Demo MutationObserver

Slide 41

Slide 41 text

Content Projection Copy Content copy me copy me X 8

Slide 42

Slide 42 text

Content Projection Copy Content * Demo

Slide 43

Slide 43 text

Content Projection Move Content content 1 content 2 content 3 content 3 content 2 content 1

Slide 44

Slide 44 text

Content Projection Move Content content 1 content 2 content 3 title 1 title 2 title 3 content 1

Slide 45

Slide 45 text

Content Projection Move Content Eager Load Lazy Load VS

Slide 46

Slide 46 text

Dynamic Component

Slide 47

Slide 47 text

Dynamic Component The missing compile Template String compile Dynamic Component

Slide 48

Slide 48 text

Dynamic Component The missing compile export function createCompiler(compilerFactory: CompilerFactory) { return compilerFactory.createCompiler([compilerOptions]); } providers: [ { provide: COMPILER_OPTIONS, useValue: compilerOptions, multi: true }, { provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS] }, { provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory] } ], const dynamicComponent = Component({ template })( class { context = context; } ); const dynamicModule = NgModule({ declarations : [ dynamicComponent ], exports : [ dynamicComponent ], entryComponents: [ dynamicComponent ], imports : [ FormsModule ] })(class DynamicModule { }); this.compiledModule = await this.compiler.compileModuleAndAllComponentsAsync(dynamicModule); DEMO

Slide 49

Slide 49 text

Dynamic Component Ivy Render import { Component, ɵrenderComponent as renderComponent, Injector, ɵLifecycleHooksFeature as LifecycleHooksFeature } from "@angular/core"; @Component({ selector : 'app-root', templateUrl: './app.component.html', styleUrls : ['./app.component.css'] }) export class AppComponent { constructor(private injector: Injector) { import('./dynamic/dynamic.component').then(({ DynamicComponent }) => { renderComponent(DynamicComponent, { injector, host: '#slot', hostFeatures: [LifecycleHooksFeature] }); }); } }

Slide 50

Slide 50 text

No content