av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

創(chuàng)新互聯(lián)Angular教程:Angular升級說明

從 AngularJS 升級到 Angular

Angular是現(xiàn)在和未來的 Angular 名稱。

AngularJS是所有 1.x 版本的 Angular 的名稱。

有很多大型 AngularJS 應(yīng)用。在遷移到 Angular 之前,請始終考慮其業(yè)務(wù)案例。該案例的一個重要部分是遷移的時間和精力。本指南描述了用于將 AngularJS 項目高效遷移到 Angular 平臺的內(nèi)置工具,一次一個。

有些應(yīng)用可能比其它的升級起來簡單,還有一些方法能讓把這項工作變得更簡單。 即使在正式開始升級過程之前,可以提前準備 AngularJS 的程序,讓它向 Angular 看齊。 這些準備步驟幾乎都是關(guān)于如何讓代碼更加松耦合、更有可維護性,以及用現(xiàn)代開發(fā)工具提高速度的。 這意味著,這種準備工作不僅能讓最終的升級變得更簡單,而且還能提升 AngularJS 程序的質(zhì)量。

成功升級的關(guān)鍵之一是增量式的實現(xiàn)它,通過在同一個應(yīng)用中一起運行這兩個框架,并且逐個把 AngularJS 的組件遷移到 Angular 中。 這意味著可以在不必打斷其它業(yè)務(wù)的前提下,升級更大、更復(fù)雜的應(yīng)用程序,因為這項工作可以多人協(xié)作完成,在一段時間內(nèi)逐漸鋪開。 Angular upgrade 模塊的設(shè)計目標就是讓你漸進、無縫的完成升級。

準備

AngularJS 應(yīng)用程序的組織方式有很多種。當你想把它們升級到 Angular 的時候, 有些做起來會比其它的更容易些。即使在開始升級之前,也有一些關(guān)鍵的技術(shù)和模式可以讓你將來升級時更輕松。

遵循 AngularJS 風格指南

AngularJS 風格指南收集了一些已證明能寫出干凈且可維護的 AngularJS 程序的模式與實踐。 它包含了很多關(guān)于如何書寫和組織 AngularJS 代碼的有價值信息,同樣重要的是,不應(yīng)該采用的書寫和組織 AngularJS 代碼的方式。

Angular 是一個基于 AngularJS 中最好的部分構(gòu)思出來的版本。在這種意義上,它的目標和 AngularJS 風格指南是一樣的: 保留 AngularJS 中好的部分,去掉壞的部分。當然,Angular 還做了更多。 說這些的意思是:遵循這個風格指南可以讓你寫出更接近 Angular 程序的 AngularJS 程序。

有一些特別的規(guī)則可以讓使用 Angular 的 ?upgrade/static? 模塊進行增量升級變得更簡單:

  • 單一規(guī)則 規(guī)定每個文件應(yīng)該只放一個組件。這不僅讓組件更容易瀏覽和查找,而且還讓你能逐個遷移它們的語言和框架。 在這個范例程序中,每個控制器、工廠和過濾器都位于各自的源文件中。
  • 按特性分目錄的結(jié)構(gòu)和模塊化規(guī)則在較高的抽象層定義了一些相似的原則:應(yīng)用程序中的不同部分應(yīng)該被分到不同的目錄和 NgModule 中。

如果應(yīng)用程序能用這種方式把每個特性分到一個獨立目錄中,它也就能每次遷移一個特性。 對于那些還沒有這么做的程序,強烈建議把應(yīng)用這條規(guī)則作為準備步驟。而且這也不僅僅對升級有價值, 它還是一個通用的規(guī)則,可以讓你的程序更“堅實”。

使用模塊加載器

當你把應(yīng)用代碼分解到每個文件中只放一個組件的粒度后,通常會得到一個由大量相對較小的文件組成的項目結(jié)構(gòu)。 這比組織成少量大文件要整潔得多,但如果你不得不通過 ?

{{ mainCtrl.message }}

你可以從 HTML 中移除 ?ng-app? 和 ?ng-strict-di? 指令,改為從 JavaScript 中調(diào)用 ?angular.bootstrap?,它能達到同樣效果:

angular.bootstrap(document.body, ['heroApp'], { strictDi: true });

要想把 AngularJS 應(yīng)用變成 Hybrid 應(yīng)用,就要先加載 Angular 框架。 根據(jù)準備升級到 AngularJS 中給出的步驟,選擇性的把快速入門 github 代碼倉中的代碼復(fù)制過來。

也可以通過 ?npm install @angular/upgrade --save? 命令來安裝 ?@angular/upgrade? 包,并給它添加一個到 ?@angular/upgrade/static? 包的映射。

'@angular/upgrade/static': 'npm:@angular/upgrade/fesm2015/static.mjs',

接下來,創(chuàng)建一個 ?app.module.ts? 文件,并添加下列 ?NgModule ?類:

import { DoBootstrap, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { UpgradeModule } from '@angular/upgrade/static';

@NgModule({
  imports: [
    BrowserModule,
    UpgradeModule
  ]
})
export class AppModule implements DoBootstrap {
  constructor(private upgrade: UpgradeModule) { }
  ngDoBootstrap() {
    this.upgrade.bootstrap(document.body, ['heroApp'], { strictDi: true });
  }
}

最小化的 ?NgModule ?導(dǎo)入了 ?BrowserModule?,它是每個基于瀏覽器的 Angular 應(yīng)用必備的。 它還從 ?@angular/upgrade/static? 中導(dǎo)入了 ?UpgradeModule?,它導(dǎo)出了一些服務(wù)提供者,這些提供者會用于升級、降級服務(wù)和組件。

在 ?AppModule ?的構(gòu)造函數(shù)中,使用依賴注入技術(shù)獲取了一個 ?UpgradeModule ?實例,并用它在 ?AppModule.ngDoBootstrap? 方法中啟動 AngularJS 應(yīng)用。 ?upgrade.bootstrap? 方法接受和 angular.bootstrap 完全相同的參數(shù)。

注意,你不需要在 ?@NgModule? 中加入 ?bootstrap ?聲明,因為 AngularJS 控制著該應(yīng)用的根模板。

現(xiàn)在,你就可以使用 ?platformBrowserDynamic.bootstrapModule? 方法來啟動 ?AppModule ?了。

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

platformBrowserDynamic().bootstrapModule(AppModule);

恭喜!你就要開始運行這個混合式應(yīng)用了!所有現(xiàn)存的 AngularJS 代碼會像以前一樣正常工作,但是你現(xiàn)在也同樣可以運行 Angular 代碼了。

從 AngularJS 代碼中使用 Angular 組件

一旦你開始運行混合式應(yīng)用,你就可以開始逐漸升級代碼了。一種更常見的工作模式就是在 AngularJS 的上下文中使用 Angular 的組件。 該組件可能是全新的,也可能是把原本 AngularJS 的組件用 Angular 重寫而成的。

假設(shè)你有一個用來顯示英雄信息的 Angular 組件:

import { Component } from '@angular/core';

@Component({
  selector: 'hero-detail',
  template: `
    

Windstorm details!

1
` }) export class HeroDetailComponent { }

如果你想在 AngularJS 中使用這個組件,就得用 ?downgradeComponent()? 方法把它降級。 其結(jié)果是一個 AngularJS 的指令,你可以把它注冊到 AngularJS 的模塊中:

import { HeroDetailComponent } from './hero-detail.component';

/* . . . */

import { downgradeComponent } from '@angular/upgrade/static';

angular.module('heroApp', [])
  .directive(
    'heroDetail',
    downgradeComponent({ component: HeroDetailComponent }) as angular.IDirectiveFactory
  );

默認情況下,Angular 變更檢測也會在 AngularJS 的每個 ?$digest? 周期中運行。如果你希望只在輸入屬性發(fā)生變化時才運行變更檢測,可以在調(diào)用 ?downgradeComponent()? 時把 ?propagateDigest ?設(shè)置為 ?false?。

由于 ?HeroDetailComponent ?是一個 Angular 組件,所以你必須同時把它加入 ?AppModule ?的 ?declarations ?字段中。

import { HeroDetailComponent } from './hero-detail.component';

@NgModule({
  imports: [
    BrowserModule,
    UpgradeModule
  ],
  declarations: [
    HeroDetailComponent
  ]
})
export class AppModule implements DoBootstrap {
  constructor(private upgrade: UpgradeModule) { }
  ngDoBootstrap() {
    this.upgrade.bootstrap(document.body, ['heroApp'], { strictDi: true });
  }
}

所有 Angular 組件、指令和管道都必須聲明在 NgModule 中。

最終的結(jié)果是一個叫做 ?heroDetail ?的 AngularJS 指令,你可以像用其它指令一樣把它用在 AngularJS 模板中。

注意,它在 AngularJS 中是一個名叫 ?heroDetail ?的元素型指令(?restrict: 'E'?)。 AngularJS 的元素型指令是基于它的名字匹配的。 Angular 組件中的 ?selector ?元數(shù)據(jù),在降級后的版本中會被忽略

當然,大多數(shù)組件都不像這個這么簡單。它們中很多都有輸入屬性和輸出屬性,來把它們連接到外部世界。 Angular 的英雄詳情組件帶有像這樣的輸入屬性與輸出屬性:

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'hero-detail',
  template: `
    

{{hero.name}} details!

{{hero.id}}
` }) export class HeroDetailComponent { @Input() hero!: Hero; @Output() deleted = new EventEmitter(); onDelete() { this.deleted.emit(this.hero); } }

這些輸入屬性和輸出屬性的值來自于 AngularJS 的模板,而 ?downgradeComponent()? 方法負責橋接它們:

注意,雖然你正在 AngularJS 的模板中,但卻在使用 Angular 的屬性(Attribute)語法來綁定到輸入屬性與輸出屬性。 這是降級的組件本身要求的。而表達式本身仍然是標準的 AngularJS 表達式。

在降級過的組件屬性中使用中線命名法

為降級過的組件使用 Angular 的屬性(Attribute)語法規(guī)則時有一個值得注意的例外。 它適用于由多個單詞組成的輸入或輸出屬性。在 Angular 中,你要使用小駝峰命名法綁定這些屬性:

[myHero]="hero"
(heroDeleted)="handleHeroDeleted($event)"

但是從 AngularJS 的模板中使用它們時,你得使用中線命名法:

[my-hero]="hero"
(hero-deleted)="handleHeroDeleted($event)"

?$event? 變量能被用在輸出屬性里,以訪問這個事件所發(fā)出的對象。這個案例中它是 ?Hero ?對象,因為 ?this.deleted.emit()? 函數(shù)曾把它傳了出來。

由于這是一個 AngularJS 模板,雖然它已經(jīng)有了 Angular 中綁定的屬性(Attribute),你仍可以在這個元素上使用其它 AngularJS 指令。 例如,你可以用 ?ng-repeat? 簡單的制作該組件的多份拷貝:

從 Angular 代碼使用 AngularJS 組件指令

現(xiàn)在,你已經(jīng)能在 Angular 中寫一個組件,并把它用于 AngularJS 代碼中了。 當你從低級組件開始移植,并往上走時,這非常有用。但在另外一些情況下,從相反的方向進行移植會更加方便: 從高級組件開始,然后往下走。這也同樣能用 ?UpgradeModule ?完成。 你可以升級AngularJS 組件型指令,然后從 Angular 中用它們。

不是所有種類的 AngularJS 指令都能升級。該指令必須是一個嚴格的組件型指令,具有上面的準備指南中描述的那些特征。 確保兼容性的最安全的方式是 AngularJS 1.5 中引入的組件 API。

可升級組件的簡單例子是只有一個模板和一個控制器的指令:

export const heroDetail = {
  template: `
    

Windstorm details!

1
`, controller: function HeroDetailController() { } };

你可以使用 ?UpgradeComponent ?方法來把這個組件升級到 Angular。 具體方法是創(chuàng)建一個 Angular指令,繼承 ?UpgradeComponent?,在其構(gòu)造函數(shù)中進行 ?super ?調(diào)用, 這樣你就得到一個完全升級的 AngularJS 組件,并且可以 Angular 中使用。 剩下是工作就是把它加入到 ?AppModule ?的 ?declarations ?數(shù)組。

import { Directive, ElementRef, Injector, SimpleChanges } from '@angular/core';
import { UpgradeComponent } from '@angular/upgrade/static';

@Directive({
  selector: 'hero-detail'
})
export class HeroDetailDirective extends UpgradeComponent {
  constructor(elementRef: ElementRef, injector: Injector) {
    super('heroDetail', elementRef, injector);
  }
}
@NgModule({
  imports: [
    BrowserModule,
    UpgradeModule
  ],
  declarations: [
    HeroDetailDirective,
  /* . . . */
  ]
})
export class AppModule implements DoBootstrap {
  constructor(private upgrade: UpgradeModule) { }
  ngDoBootstrap() {
    this.upgrade.bootstrap(document.body, ['heroApp'], { strictDi: true });
  }
}

升級后的組件是 Angular 的指令,而不是組件,因為 Angular 不知道 AngularJS 將在它下面創(chuàng)建元素。 Angular 所知道的是升級后的組件只是一個指令(一個標簽),Angular 不需要關(guān)心組件本身及其子元素。

升級后的組件也可能有輸入屬性和輸出屬性,它們是在原 AngularJS 組件型指令的 scope/controller 綁定中定義的。 當你從 Angular 模板中使用該組件時,就要使用Angular 模板語法來提供這些輸入屬性和輸出屬性,但要遵循下列規(guī)則:

綁定定義

模板語法

屬性綁定

myAttribute: '@myAttribute'

表達式綁定

myOutput: '&myOutput'

單向綁定

myValue: '

雙向綁定

myValue: '=myValue'

用作雙向綁定:。
由于大多數(shù) AngularJS 的雙向綁定實際上只是單向綁定,因此通常寫成  也夠用了。

舉個例子,假設(shè) AngularJS 中有一個表示“英雄詳情”的組件型指令,它帶有一個輸入屬性和一個輸出屬性:

export const heroDetail = {
  bindings: {
    hero: '<',
    deleted: '&'
  },
  template: `
    

{{$ctrl.hero.name}} details!

{{$ctrl.hero.id}}
`, controller: function HeroDetailController() { this.onDelete = () => { this.deleted(this.hero); }; } };

你可以把這個組件升級到 Angular,然后使用 Angular 的模板語法提供這個輸入屬性和輸出屬性:

import { Directive, ElementRef, Injector, Input, Output, EventEmitter } from '@angular/core';
import { UpgradeComponent } from '@angular/upgrade/static';
import { Hero } from '../hero';

@Directive({
  selector: 'hero-detail'
})
export class HeroDetailDirective extends UpgradeComponent {
  @Input() hero: Hero;
  @Output() deleted: EventEmitter;

  constructor(elementRef: ElementRef, injector: Injector) {
    super('heroDetail', elementRef, injector);
  }
}
import { Component } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'my-container',
  template: `
    

Tour of Heroes

` }) export class ContainerComponent { hero = new Hero(1, 'Windstorm'); heroDeleted(hero: Hero) { hero.name = 'Ex-' + hero.name; } }

把 AngularJS 的內(nèi)容投影到 Angular 組件中

如果你在 AngularJS 模板中使用降級后的 Angular 組件時,可能會需要把模板中的一些內(nèi)容投影進那個組件。 這也是可能的,雖然在 Angular 中并沒有透傳(transclude)這樣的東西,但它有一個非常相似的概念,叫做內(nèi)容投影。 ?UpgradeModule ?也能讓這兩個特性實現(xiàn)互操作。

Angular 的組件通過使用 ?? 標簽來支持內(nèi)容投影。下面是這類組件的一個例子:

import { Component, Input } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'hero-detail',
  template: `
    

{{hero.name}}

` }) export class HeroDetailComponent { @Input() hero!: Hero; }

當從 AngularJS 中使用該組件時,你可以為它提供內(nèi)容。正如它們將在 AngularJS 中被透傳一樣, 它們也在 Angular 中被投影到了 ?? 標簽所在的位置:

{{mainCtrl.hero.description}}

當 AngularJS 的內(nèi)容被投影到 Angular 組件中時,它仍然留在“AngularJS 王國”中,并被 AngularJS 框架管理著。

把 Angular 的內(nèi)容透傳進 AngularJS 的組件型指令

就像可以把 AngularJS 的內(nèi)容投影進 Angular 組件一樣,你也能把 Angular 的內(nèi)容透傳進 AngularJS 的組件, 但不管怎樣,你都要使用它們升級過的版本。

如果一個 AngularJS 組件型指令支持透傳,它就會在自己的模板中使用 ?ng-transclude? 指令標記出透傳到的位置:

export const heroDetail = {
  bindings: {
    hero: '='
  },
  template: `
    

{{$ctrl.hero.name}}

`, transclude: true };

如果你升級這個組件,并把它用在 Angular 中,你就能把準備透傳的內(nèi)容放進這個組件的標簽中。

import { Component } from '@angular/core';
import { Hero } from '../hero';

@Component({
  selector: 'my-container',
  template: `
    
      
      

{{hero.description}}

` }) export class ContainerComponent { hero = new Hero(1, 'Windstorm', 'Specific powers of controlling winds'); }

讓 AngularJS 中的依賴可被注入到 Angular

當運行一個混合式應(yīng)用時,可能會遇到這種情況:你需要把某些 AngularJS 的依賴注入到 Angular 代碼中。 這可能是因為某些業(yè)務(wù)邏輯仍然在 AngularJS 服務(wù)中,或者需要某些 AngularJS 的內(nèi)置服務(wù),比如 ?$location? 或 ?$timeout?。

在這些情況下,把一個 AngularJS 提供者升級到Angular 也是有可能的。這就讓它將來有可能被注入到 Angular 代碼中的某些地方。 比如,你可能在 AngularJS 中有一個名叫 ?HeroesService ?的服務(wù):

import { Hero } from '../hero';

export class HeroesService {
  get() {
    return [
      new Hero(1, 'Windstorm'),
      new Hero(2, 'Spiderman')
    ];
  }
}

你可以用 Angular 的工廠提供者升級該服務(wù), 它從 AngularJS 的 ?$injector? 請求服務(wù)。

很多開發(fā)者都喜歡在一個獨立的 ?ajs-upgraded-providers.ts? 中聲明這個工廠提供者,以便把它們都放在一起,這樣便于引用、創(chuàng)建新的以及在升級完畢時刪除它們。

同時,建議導(dǎo)出 ?heroesServiceFactory ?函數(shù),以便 AOT 編譯器可以拿到它們。

注意:這個工廠中的字符串 'heroes' 指向的是 AngularJS 的 ?HeroesService?。 AngularJS 應(yīng)用中通常使用服務(wù)名作為令牌,比如 'heroes',并為其追加 'Service' 后綴來創(chuàng)建其類名。

import { HeroesService } from './heroes.service';

export function heroesServiceFactory(i: any) {
  return i.get('heroes');
}

export const heroesServiceProvider = {
  provide: HeroesService,
  useFactory: heroesServiceFactory,
  deps: ['$injector']
};

然后,你就可以把這個服務(wù)添加到 ?@NgModule? 中來把它暴露給 Angular:

import { heroesServiceProvider } from './ajs-upgraded-providers';

@NgModule({
  imports: [
    BrowserModule,
    UpgradeModule
  ],
  providers: [
    heroesServiceProvider
  ],
/* . . . */
})
export class AppModule implements DoBootstrap {
  constructor(private upgrade: UpgradeModule) { }
  ngDoBootstrap() {
    this.upgrade.bootstrap(document.body, ['heroApp'], { strictDi: true });
  }
}

然后在組件的構(gòu)造函數(shù)中使用該服務(wù)的類名作為類型注解注入到組件中,從而在組件中使用它:

import { Component } from '@angular/core';
import { HeroesService } from './heroes.service';
import { Hero } from '../hero';

@Component({
  selector: 'hero-detail',
  template: `
    

{{hero.id}}: {{hero.name}}

` }) export class HeroDetailComponent { hero: Hero; constructor(heroes: HeroesService) { this.hero = heroes.get()[0]; } }

在這個例子中,你升級了服務(wù)類。當注入它時,你可以使用 TypeScript 類型注解來獲得這些額外的好處。 它沒有影響該依賴的處理過程,同時還得到了啟用靜態(tài)類型檢查的好處。 任何 AngularJS 中的服務(wù)、工廠和提供者都能被升級 —— 盡管這不是必須的。

讓 Angular 的依賴能被注入到 AngularJS 中

除了能升級 AngularJS 依賴之外,你還能降級Angular 的依賴,以便在 AngularJS 中使用它們。 當你已經(jīng)開始把服務(wù)移植到 Angular 或在 Angular 中創(chuàng)建新服務(wù),但同時還有一些用 AngularJS 寫成的組件時,這會非常有用。

例如,你可能有一個 Angular 的 ?Heroes ?服務(wù):

import { Injectable } from '@angular/core';
import { Hero } from '../hero';

@Injectable()
export class Heroes {
  get() {
    return [
      new Hero(1, 'Windstorm'),
      new Hero(2, 'Spiderman')
    ];
  }
}

仿照 Angular 組件,把該提供者加入 ?NgModule ?的 ?providers ?列表中,以注冊它。

import { Heroes } from './heroes';

@NgModule({
  imports: [
    BrowserModule,
    UpgradeModule
  ],
  providers: [ Heroes ]
})
export class AppModule implements DoBootstrap {
  constructor(private upgrade: UpgradeModule) { }
  ngDoBootstrap() {
    this.upgrade.bootstrap(document.body, ['heroApp'], { strictDi: true });
  }
}

現(xiàn)在,用 ?downgradeInjectable()? 來把 Angular 的 ?Heroes ?包裝成AngularJS 的工廠函數(shù),并把這個工廠注冊進 AngularJS 的模塊中。 依賴在 AngularJS 中的名字你可以自己定:

import { Heroes } from './heroes';
/* . . . */
import { downgradeInjectable } from '@angular/upgrade/static';

angular.module('heroApp', [])
  .factory('heroes', downgradeInjectable(Heroes))
  .component('heroDetail', heroDetailComponent);

此后,該服務(wù)就能被注入到 AngularJS 代碼中的任何地方了:

export const heroDetailComponent = {
  template: `
    

{{$ctrl.hero.id}}: {{$ctrl.hero.name}}

`, controller: ['heroes', function(heroes: Heroes) { this.hero = heroes.get()[0]; }] };

惰性加載 AngularJS

在構(gòu)建應(yīng)用時,你需要確保只在必要的時候才加載所需的資源,無論是加載靜態(tài)資產(chǎn)(Asset)還是代碼。要確保任何事都盡量推遲到必要時才去做,以便讓應(yīng)用更高效的運行。當要在同一個應(yīng)用中運行不同的框架時,更是如此。

惰性加載是一項技術(shù),它會推遲到使用時才加載所需靜態(tài)資產(chǎn)和代碼資源。這可以減少啟動時間、提高效率,特別是要在同一個應(yīng)用中運行不同的框架時。

當你采用混合式應(yīng)用的方式將大型應(yīng)用從 AngularJS 遷移到 Angular 時,你首先要遷移一些最常用的特性,并且只在必要的時候才使用那些不太常用的特性。這樣做有助于確保應(yīng)用程序在遷移過程中仍然能為用戶提供無縫的體驗。

在大多數(shù)需要同時用 Angular 和 AngularJS 渲染應(yīng)用的環(huán)境中,這兩個框架都會包含在發(fā)送給客戶端的初始發(fā)布包中。這會導(dǎo)致發(fā)布包的體積增大、性能降低。

當用戶停留在由 Angular 渲染的頁面上時,應(yīng)用的整體性能也會受到影響。這是因為 AngularJS 的框架和應(yīng)用仍然被加載并運行了 —— 即使它們從未被訪問過。

你可以采取一些措施來緩解這些包的大小和性能問題。通過把 AngularJS 應(yīng)用程序分離到一個單獨的發(fā)布包中,你就可以利用惰性加載技術(shù)來只在必要的時候才加載、引導(dǎo)和渲染這個 AngularJS 應(yīng)用。這種策略減少了你的初始發(fā)布包大小,推遲了同時加載兩個框架的潛在影響 —— 直到絕對必要時才加載,以便讓你的應(yīng)用盡可能高效地運行。

下面的步驟介紹了應(yīng)該如何去做:

  • 為 AngularJS 發(fā)布包設(shè)置一個回調(diào)函數(shù)。
  • 創(chuàng)建一個服務(wù),以便惰性加載并引導(dǎo)你的 AngularJS 應(yīng)用。
  • 為 AngularJS 內(nèi)容創(chuàng)建一個可路由的組件
  • 為 AngularJS 特有的 URL 創(chuàng)建自定義的 ?matcher ?函數(shù),并為 AngularJS 的各個路由配上帶有自定義匹配器的 Angular 路由器。

為惰性加載 AngularJS 創(chuàng)建一個服務(wù)

在 Angular 的版本 8 中,惰性加載代碼只需使用動態(tài)導(dǎo)入語法 ?import('...')? 即可。在這個應(yīng)用中,你創(chuàng)建了一個新服務(wù),它使用動態(tài)導(dǎo)入技術(shù)來惰性加載 AngularJS。

import { Injectable } from '@angular/core';
import * as angular from 'angular';

@Injectable({
  providedIn: 'root'
})
export class LazyLoaderService {
  private app: angular.auto.IInjectorService | undefined;

  load(el: HTMLElement): void {
    import('./angularjs-app').then(app => {
      try {
        this.app = app.bootstrap(el);
      } catch (e) {
        console.error(e);
      }
    });
  }

  destroy() {
    if (this.app) {
      this.app.get('$rootScope').$destroy();
    }
  }
}

該服務(wù)使用 ?import()? 方法惰性加載打包好的 AngularJS 應(yīng)用。這會減少應(yīng)用初始包的大小,因為你尚未加載用戶目前不需要的代碼。你還要提供一種方法,在加載完畢后手動啟動它。AngularJS 提供了一種使用 angular.bootstrap() 方法并傳入一個 HTML 元素來手動引導(dǎo)應(yīng)用的方法。你的 AngularJS 應(yīng)用也應(yīng)該公開一個用來引導(dǎo) AngularJS 應(yīng)用的 ?bootstrap ?方法。

要確保 AngularJS 應(yīng)用中的任何清理工作都觸發(fā)過(比如移除全局監(jiān)聽器),你還可以實現(xiàn)一個方法來調(diào)用 ?$rootScope.destroy()? 方法。

import * as angular from 'angular';
import 'angular-route';

const appModule = angular.module('myApp', [
  'ngRoute'
])
.config(['$routeProvider', '$locationProvider',
  function config($routeProvider: angular.route.IRouteProvider,
                  $locationProvider: angular.ILocationProvider) {
    $locationProvider.html5Mode(true);

    $routeProvider.
      when('/users', {
        template: `
          

Users Page

` }). otherwise({ template: '' }); }] ); export function bootstrap(el: HTMLElement) { return angular.bootstrap(el, [appModule.name]); }

你的 AngularJS 應(yīng)用只配置了渲染內(nèi)容所需的那部分路由。而 Angular 路由器會處理應(yīng)用中其余的路由。你的 Angular 應(yīng)用中會調(diào)用公開的 ?bootstrap ?方法,讓它在加載完發(fā)布包之后引導(dǎo) AngularJS 應(yīng)用。

注意:當 AngularJS 加載并引導(dǎo)完畢后,監(jiān)聽器(比如路由配置中的那些監(jiān)聽器)會繼續(xù)監(jiān)聽路由的變化。為了確保當 AngularJS 尚未顯示時先關(guān)閉監(jiān)聽器,請在 $routeProvider 中配置一個渲染空模板 ?otherwise ?選項。這里假設(shè) Angular 將處理所有其它路由。

創(chuàng)建一個用來渲染 AngularJS 內(nèi)容的組件

在 Angular 應(yīng)用中,你需要一個組件作為 AngularJS 內(nèi)容的占位符。該組件使用你創(chuàng)建的服務(wù),并在組件初始化完成后加載并引導(dǎo)你的 AngularJS 應(yīng)用。

import { Component, OnInit, OnDestroy, ElementRef } from '@angular/core';
import { LazyLoaderService } from '../lazy-loader.service';

@Component({
  selector: 'app-angular-js',
  template: '
' }) export class AngularJSComponent implements OnInit, OnDestroy { constructor( private lazyLoader: LazyLoaderService, private elRef: ElementRef ) {} ngOnInit() { this.lazyLoader.load(this.elRef.nativeElement); } ngOnDestroy() { this.lazyLoader.destroy(); } }

當 Angular 的路由器匹配到使用 AngularJS 的路由時,會渲染 AngularJSComponent,并在 AngularJS 的 ng-view 指令中渲染內(nèi)容。當用戶導(dǎo)航離開本路由時,$rootScope 會在 AngularJS 應(yīng)用中被銷毀。

為那些 AngularJS 路由配置自定義路由匹配器

為了配置 Angular 的路由器,你必須為 AngularJS 的 URL 定義路由。要匹配這些 URL,你需要添加一個使用 ?matcher ?屬性的路由配置。這個 ?matcher ?允許你使用自定義模式來匹配這些 URL 路徑。Angular 的路由器會首先嘗試匹配更具體的路由,比如靜態(tài)路由和可變路由。當它找不到匹配項時,就會求助于路由配置中的自定義匹配器。如果自定義匹配器與某個路由不匹配,它就會轉(zhuǎn)到用于 "捕獲所有"(catch-all)的路由,比如 404 頁面。

下面的例子給 AngularJS 路由定義了一個自定義匹配器函數(shù)。

export function isAngularJSUrl(url: UrlSegment[]) {
  return url.length > 0 && url[0].path.startsWith('users') ? ({consumed: url}) : null;
}

下列代碼往你的路由配置中添加了一個路由對象,其 ?matcher ?屬性是這個自定義匹配器,而 ?component ?屬性為 ?AngularJSComponent?。

import { NgModule } from '@angular/core';
import { Routes, RouterModule, UrlSegment } from '@angular/router';
import { AngularJSComponent } from './angular-js/angular-js.component';
import { HomeComponent } from './home/home.component';
import { App404Component } from './app404/app404.component';

// Match any URL that starts with `users`
export function isAngularJSUrl(url: UrlSegment[]) {
  return url.length > 0 && url[0].path.startsWith('users') ? ({consumed: url}) : null;
}

export const routes: Routes = [
  // Routes rendered by Angular
  { path: '', component: HomeComponent },

  // AngularJS routes
  { matcher: isAngularJSUrl, component: AngularJSComponent },

  // Catch-all route
  { path: '**', component: App404Component }
];

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

當你的應(yīng)用匹配上需要 AngularJS 的路由時,AngularJS 應(yīng)用就會被加載并引導(dǎo)。AngularJS 路由會匹配必要的 URL 以渲染它們的內(nèi)容,而接下來你的應(yīng)用就會同時運行 AngularJS 和 Angular 框架。

使用統(tǒng)一的 Angular 位置服務(wù)(Location)

在 AngularJS 中,$location 服務(wù)會處理所有路由配置和導(dǎo)航工作,并對各個 URL 進行編碼和解碼、重定向、以及與瀏覽器 API 交互。Angular 在所有這些任務(wù)中都使用了自己的底層服務(wù) ?Location?。

當你從 AngularJS 遷移到 Angular 時,你會希望把盡可能多的責任移交給 Angular,以便利用新的 API。為了幫你完成這種轉(zhuǎn)換,Angular 提供了 ?LocationUpgradeModule?。該模塊支持統(tǒng)一位置服務(wù),可以把 AngularJS 中 ?$location? 服務(wù)的職責轉(zhuǎn)給 Angular 的 ?Location ?服務(wù)。

要使用 ?LocationUpgradeModule?,就會從 ?@angular/common/upgrade? 中導(dǎo)入此符號,并使用靜態(tài)方法 ?LocationUpgradeModule.config()? 把它添加到你的 ?AppModule ?導(dǎo)入表(?imports?)中。

// Other imports ...
import { LocationUpgradeModule } from '@angular/common/upgrade';

@NgModule({
  imports: [
    // Other NgModule imports...
    LocationUpgradeModule.config()
  ]
})
export class AppModule {}

?LocationUpgradeModule.config()? 方法接受一個配置對象,該對象的 ?useHash ?為 ?LocationStrategy?,?hashPrefix ?為 URL 前綴。

?useHash ?屬性默認為 ?false?,而 ?hashPrefix ?默認為空 ?string?。傳遞配置對象可以覆蓋默認值。

LocationUpgradeModule.config({
  useHash: true,
  hashPrefix: '!'
})

這會為 AngularJS 中的 ?$location? 提供者注冊一個替代品。一旦注冊成功,導(dǎo)航過程中所有由 AngularJS 觸發(fā)的導(dǎo)航、路由廣播消息以及任何必需的變更檢測周期都會改由 Angular 進行處理。這樣,你就可以通過這個唯一的途徑在此混合應(yīng)用的兩個框架間進行導(dǎo)航了。

要想在 AngularJS 中使用 ?$location? 服務(wù)作為提供者,你需要使用一個工廠提供者來降級 ?$locationShim?。

// Other imports ...
import { $locationShim } from '@angular/common/upgrade';
import { downgradeInjectable } from '@angular/upgrade/static';

angular.module('myHybridApp', [...])
  .factory('$location', downgradeInjectable($locationShim));

一旦引入了 Angular 路由器,你只要使用 Angular 路由器就可以通過統(tǒng)一位置服務(wù)來觸發(fā)導(dǎo)航了,同時,你仍然可以通過 AngularJS 和 Angular 進行導(dǎo)航。

PhoneCat 升級教程

在本節(jié)和下節(jié)中,你將看一個完整的例子,它使用 ?upgrade ?模塊準備和升級了一個應(yīng)用程序。 該應(yīng)用就是來自原 AngularJS 教程中的Angular PhoneCat。 那是我們很多人當初開始 Angular 探險之旅的地方。 現(xiàn)在,你會看到如何把該應(yīng)用帶入 Angular 的美麗新世界。

這期間,你將學(xué)到如何在實踐中應(yīng)用準備指南中列出的那些重點步驟: 你先讓該應(yīng)用向 Angular 看齊,然后為它引入 SystemJS 模塊加載器和 TypeScript。

本教程基于 ?angular-phonecat? 教程的 1.5.x 版本,該教程保存在代碼倉庫的 1.5-snapshot 分支中。接下來,克隆 angular-phonecat 代碼倉庫,check out ?1.5-snapshot? 分支并應(yīng)用這些步驟。

在項目結(jié)構(gòu)方面,工作的起點是這樣的:

這確實是一個很好地起點。這些代碼使用了 AngularJS 1.5 的組件 API,并遵循了 AngularJS 風格指南進行組織, 在成功升級之前,這是一個很重要的準備步驟。

  • 每個組件、服務(wù)和過濾器都在它自己的源文件中 —— 就像單一規(guī)則所要求的。
  • ?core?、?phone-detail? 和 ?phone-list? 模塊都在它們自己的子目錄中。那些子目錄除了包含 HTML 模板之外,還包含 JavaScript 代碼,它們共同完成一個特性。 這是按特性分目錄的結(jié)構(gòu) 和模塊化規(guī)則所要求的。
  • 單元測試都和應(yīng)用代碼在一起,它們很容易找到。就像規(guī)則 組織測試文件中要求的那樣。

切換到 TypeScript

因為你將使用 TypeScript 編寫 Angular 的代碼,所以在開始升級之前,先要把 TypeScript 的編譯器設(shè)置好。

你還將開始逐步淘汰 Bower 包管理器,換成 NPM。后面你將使用 NPM 來安裝新的依賴包,并最終從項目中移除 Bower。

先把 TypeScript 包安裝到項目中。

npm i typescript --save-dev

還要為那些沒有自帶類型信息的庫(比如 AngularJS、AngularJS Material 和 Jasmine)安裝類型定義文件。

對于 PhoneCat 應(yīng)用,我們可以運行下列命令來安裝必要的類型定義文件:

npm install @types/jasmine @types/angular @types/angular-animate @types/angular-aria @types/angular-cookies @types/angular-mocks @types/angular-resource @types/angular-route @types/angular-sanitize --save-dev

如果你正在使用 AngularJS Material,你可以通過下列命令安裝其類型定義:

npm install @types/angular-material --save-dev

你還應(yīng)該要往項目目錄下添加一個 ?tsconfig.json? 文件,?tsconfig.json? 文件會告訴 TypeScript 編譯器如何把 TypeScript 文件轉(zhuǎn)成 ES5 代碼,并打包進 CommonJS 模塊中。

最后,你應(yīng)該把下列 npm 腳本添加到 ?package.json? 中,用于把 TypeScript 文件編譯成 JavaScript (根據(jù) ?tsconfig.json? 的配置):

"scripts": {
  "tsc": "tsc",
  "tsc:w": "tsc -w",
  ...

現(xiàn)在,從命令行中用監(jiān)視模式啟動 TypeScript 編譯器:

npm run tsc:w

讓這個進程一直在后臺運行,監(jiān)聽任何變化并自動重新編譯。

接下來,把 JavaScript 文件轉(zhuǎn)換成 TypeScript 文件。 由于 TypeScript 是 ECMAScript 2015 的一個超集,而 ES2015 又是 ECMAScript 5 的超集,所以你可以簡單的把文件的擴展名從 ?.js? 換成 ?.ts?, 它們還是會像以前一樣工作。由于 TypeScript 編譯器仍在運行,它會為每一個 ?.ts? 文件生成對應(yīng)的 ?.js? 文件,而真正運行的是編譯后的 ?.js? 文件。 如果你用 ?npm start? 開啟了本項目的 HTTP 服務(wù)器,你會在瀏覽器中看到一個功能完好的應(yīng)用。

有了 TypeScript,你就可以從它的一些特性中獲益了。此語言可以為 AngularJS 應(yīng)用提供很多價值。

首先,TypeScript 是一個 ES2015 的超集。任何以前用 ES5 寫的程序(就像 PhoneCat 范例)都可以開始通過 TypeScript 納入那些添加到 ES2015 中的新特性。 這包括 ?let?、?const?、箭頭函數(shù)、函數(shù)默認參數(shù)以及解構(gòu)(destructure)賦值。

你能做的另一件事就是把類型安全添加到代碼中。這實際上已經(jīng)部分完成了,因為你已經(jīng)安裝了 AngularJS 的類型定義。 TypeScript 會幫你檢查是否正確調(diào)用了 AngularJS 的 API,—— 比如往 Angular 模塊中注冊組件。

你還能開始把類型注解添加到自己的代碼中,來從 TypeScript 的類型系統(tǒng)中獲得更多幫助。 比如,你可以給 ?checkmark ?過濾器加上注解,表明它期待一個 ?boolean ?類型的參數(shù)。 這可以更清楚的表明此過濾器打算做什么

angular.
  module('core').
  filter('checkmark', () => {
    return (input: boolean) => input ? '\u2713' : '\u2718';
  });

在 ?Phone ?服務(wù)中,你可以明確的把 ?$resource?&nb
當前標題:創(chuàng)新互聯(lián)Angular教程:Angular升級說明
當前鏈接:http://uogjgqi.cn/article/djgeojo.html

掃二維碼與項目經(jīng)理溝通

我們在微信上24小時期待你的聲音

解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流