Angular advanced features

Master Angular advanced features, 提升application performance and 可maintenance性

Angular advanced featuresoverview

Angular providing了 many 种advanced features, 用于提升application performance, 可maintenance性 and user体验. 这些featuresincluding懒加载, 预加载策略, 动态component, 国际化, 单元test and 端 to 端testetc..

提示

合理using Angular advanced features可以显著提升application performance and Developmentefficiency, 但需要根据application 具体requirementsfor选择 and configuration.

1. 懒加载

懒加载 is 指 in 需要时才加载module, 而不 is in application启动时就加载所 has module. 这可以显著reducingapplication 初始加载时间.

1.1 configuration懒加载

// app-routing.module.ts
const routes: Routes = [
  // otherroutingconfiguration
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) // 懒加载 AdminModule
  }
];

1.2 creation懒加载module

ng generate module admin --route admin --module app

1.3 懒加载module routingconfiguration

// admin-routing.module.ts
const routes: Routes = [
  { path: '', component: AdminComponent },
  { path: 'users', component: UsermanagementmentComponent },
  { path: 'products', component: ProductmanagementmentComponent }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class AdminRoutingModule { }

2. 预加载策略

预加载策略 is 指 in application启动 after , in after 台预加载懒加载module, 以提升user体验. Angular providing了 many 种预加载策略.

2.1 in 置预加载策略

// app-routing.module.ts
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  // routingconfiguration
];

@NgModule({
  imports: [RouterModule.forRoot(routes, {
    preloadingStrategy: PreloadAllModules // 预加载所 has 懒加载module
  })],
  exports: [RouterModule]
})
export class AppRoutingModule { }

2.2 自定义预加载策略

// selective-preload-strategy.ts
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';

export class SelectivePreloadStrategy implements PreloadingStrategy {
  preload(route: Route, load: () => Observable): Observable {
    if (route.data && route.data['preload']) {
      return load(); // 预加载该module
    }
    return of(null); // 不预加载该module
  }
}

// app-routing.module.ts
const routes: Routes = [
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
    data: { preload: true } // 标记 for 需要预加载
  },
  {
    path: 'user',
    loadChildren: () => import('./user/user.module').then(m => m.UserModule),
    data: { preload: false } // 标记 for 不需要预加载
  }
];

@NgModule({
  providers: [SelectivePreloadStrategy],
  imports: [RouterModule.forRoot(routes, {
    preloadingStrategy: SelectivePreloadStrategy // using自定义预加载策略
  })],
  exports: [RouterModule]
})
export class AppRoutingModule { }

3. 动态component

动态component is 指 in run时根据条件creation and 销毁 component. 这可以用于implementation模态框, 弹窗, 动态表单etc.functions.

3.1 creation动态component

// dynamic-component-host.directive.ts
import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[appDynamicComponentHost]'
})
export class DynamicComponentHostDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

// dynamic-component.component.ts
import { Component, ComponentFactoryResolver, ViewChild, OnInit } from '@angular/core';
import { DynamicComponentHostDirective } from './dynamic-component-host.directive';
import { HelloComponent } from './hello.component';
import { GoodbyeComponent } from './goodbye.component';

@Component({
  selector: 'app-dynamic-component',
  template: `
    

动态component

` }) export class DynamicComponentComponent implements OnInit { @ViewChild(DynamicComponentHostDirective, { static: true }) hostDirective: DynamicComponentHostDirective; constructor(private componentFactoryResolver: ComponentFactoryResolver) { } ngOnInit(): void { } loadHelloComponent() { const componentFactory = this.componentFactoryResolver.resolveComponentFactory(HelloComponent); const viewContainerRef = this.hostDirective.viewContainerRef; viewContainerRef.clear(); // 清除之 before component viewContainerRef.createComponent(componentFactory); // creation new component } loadGoodbyeComponent() { const componentFactory = this.componentFactoryResolver.resolveComponentFactory(GoodbyeComponent); const viewContainerRef = this.hostDirective.viewContainerRef; viewContainerRef.clear(); // 清除之 before component viewContainerRef.createComponent(componentFactory); // creation new component } } // app.module.ts import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; import { DynamicComponentComponent } from './dynamic-component/dynamic-component.component'; import { HelloComponent } from './dynamic-component/hello.component'; import { GoodbyeComponent } from './dynamic-component/goodbye.component'; import { DynamicComponentHostDirective } from './dynamic-component/dynamic-component-host.directive'; @NgModule({ declarations: [ AppComponent, DynamicComponentComponent, HelloComponent, GoodbyeComponent, DynamicComponentHostDirective ], imports: [BrowserModule], providers: [], bootstrap: [AppComponent], entryComponents: [HelloComponent, GoodbyeComponent] // register动态component }) export class AppModule { }

4. 国际化

国际化 is 指将application适配 to 不同 language and 地区. Angular providing了 in 置 国际化support.

4.1 installation国际化依赖

npm install @angular/localize --save

4.2 标记可翻译文本

// using i18n 指令标记可翻译文本

欢迎来 to 我 application

这 is a using Angular Development application.

// using i18n-placeholder 标记占位符

4.3 提取可翻译文本

ng extract-i18n --output-path src/locale

4.4 creationlanguagefile

copy生成 messages.xlf file, 并rename for messages.zh.xlf, 然 after 翻译其in 文本.

4.5 configuration many languagesupport

// angular.json
{
  "projects": {
    "my-app": {
      "i18n": {
        "sourceLocale": "en-US",
        "locales": {
          "zh": {
            "translation": "src/locale/messages.zh.xlf"
          }
        }
      },
      "architect": {
        "build": {
          "options": {
            "localize": true
          }
        }
      }
    }
  }
}

5. 单元test

单元test is 指testapplicationin 单个component, service or 指令. Angular in 置了 for 单元test support, using Jasmine and Karma.

5.1 run单元test

ng test

5.2 creation单元test

// app.component.spec.ts
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

  it('应该creationapplication', () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.componentInstance;
    expect(app).toBeTruthy();
  });

  it(`应该 has 标题 'my-app'`, () => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.componentInstance;
    expect(app.title).toEqual('my-app');
  });

  it('应该 in 页面in显示标题', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.nativeElement;
    expect(compiled.querySelector('.content span').textContent).toContain('my-app app is run!');
  });
});

6. 端 to 端test

端 to 端test is 指test整个application 流程, from user 角度mockuser交互. Angular using Protractor for端 to 端test.

6.1 run端 to 端test

ng e2e

6.2 creation端 to 端test

// app.e2e-spec.ts
import { browser, by, element } from 'protractor';

describe('workspace-project App', () => {
  beforeEach(() => {
    browser.get(browser.baseUrl);
  });

  it('应该显示欢迎message', () => {
    expect(element(by.css('h1')).getText()).toEqual('欢迎来 to 我 application');
  });

  it('应该导航 to 关于页面', () => {
    element(by.linkText('About Us')).click();
    expect(element(by.css('h2')).getText()).toEqual('About Us');
  });
});

7. performanceoptimization

Angular providing了 many 种performanceoptimizationtechniques, 用于提升application runperformance.

7.1 using OnPush 变更检测策略

// component.ts
@Component({
  selector: 'app-performance-component',
  templateUrl: './performance-component.html',
  changeDetection: ChangeDetectionStrategy.OnPush // using OnPush 变更检测策略
})
export class PerformanceComponent {
  // componentcode
}

7.2 using trackBy optimization ngFor

// template.html
  • {{ item.name }}
// component.ts trackByFn(index: number, item: any): number { return item.id; // 返回唯一标识符 }

7.3 using虚拟滚动

npm install @angular/cdk --save
// module.ts
import { ScrollingModule } from '@angular/cdk/scrolling';

@NgModule({
  imports: [
    // othermodule
    ScrollingModule
  ]
})
export class AppModule { }

// template.html

  
{{ item.name }}

8. best practices

  1. 合理using懒加载 and 预加载策略, 提升application 初始加载速度
  2. using OnPush 变更检测策略, reducing不必要 变更检测
  3. using trackBy optimization ngFor performance
  4. for 于 big 型list, using虚拟滚动
  5. for application添加适当 单元test and 端 to 端test
  6. 考虑国际化support, 使applicationable to适应不同 language and 地区
  7. using动态componentimplementation complex 交互functions
  8. 定期runperformanceanalysistool, optimizationapplicationperformance

练习 1: implementation懒加载

  1. creation一个名 for dashboard 懒加载module
  2. in 懒加载moduleincreation两个component: DashboardComponent and StatisticsComponent
  3. configuration懒加载routing
  4. test懒加载 is 否正常工作

练习 2: using OnPush 变更检测策略

  1. creation一个component, 并设置 changeDetection: ChangeDetectionStrategy.OnPush
  2. in componentinusing @Input 接收data
  3. testcomponent 变更检测behavior