日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
創(chuàng)新互聯(lián)Angular教程:Angular升級(jí)說明

從 AngularJS 升級(jí)到 Angular

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

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

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

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

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

準(zhǔn)備

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

遵循 AngularJS 風(fēng)格指南

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

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

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

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

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

使用模塊加載器

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

{{ mainCtrl.message }}

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

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

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

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

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

接下來,創(chuàng)建一個(gè) ?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?,它是每個(gè)基于瀏覽器的 Angular 應(yīng)用必備的。 它還從 ?@angular/upgrade/static? 中導(dǎo)入了 ?UpgradeModule?,它導(dǎo)出了一些服務(wù)提供者,這些提供者會(huì)用于升級(jí)、降級(jí)服務(wù)和組件。

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

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

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

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

platformBrowserDynamic().bootstrapModule(AppModule);

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

從 AngularJS 代碼中使用 Angular 組件

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

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

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

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

Windstorm details!

1
` }) export class HeroDetailComponent { }

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

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

/* . . . */

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

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

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

由于 ?HeroDetailComponent ?是一個(gè) Angular 組件,所以你必須同時(shí)把它加入 ?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é)果是一個(gè)叫做 ?heroDetail ?的 AngularJS 指令,你可以像用其它指令一樣把它用在 AngularJS 模板中。

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

當(dāng)然,大多數(shù)組件都不像這個(gè)這么簡(jiǎn)單。它們中很多都有輸入屬性和輸出屬性,來把它們連接到外部世界。 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()? 方法負(fù)責(zé)橋接它們:

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

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

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

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

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

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

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

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

從 Angular 代碼使用 AngularJS 組件指令

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

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

可升級(jí)組件的簡(jiǎn)單例子是只有一個(gè)模板和一個(gè)控制器的指令:

export const heroDetail = {
  template: `
    

Windstorm details!

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

你可以使用 ?UpgradeComponent ?方法來把這個(gè)組件升級(jí)到 Angular。 具體方法是創(chuàng)建一個(gè) Angular指令,繼承 ?UpgradeComponent?,在其構(gòu)造函數(shù)中進(jìn)行 ?super ?調(diào)用, 這樣你就得到一個(gè)完全升級(jí)的 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 });
  }
}

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

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

綁定定義

模板語(yǔ)法

屬性綁定

myAttribute: '@myAttribute'

表達(dá)式綁定

myOutput: '&myOutput'

單向綁定

myValue: '

雙向綁定

myValue: '=myValue'

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

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

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

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

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

你可以把這個(gè)組件升級(jí)到 Angular,然后使用 Angular 的模板語(yǔ)法提供這個(gè)輸入屬性和輸出屬性:

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 模板中使用降級(jí)后的 Angular 組件時(shí),可能會(huì)需要把模板中的一些內(nèi)容投影進(jìn)那個(gè)組件。 這也是可能的,雖然在 Angular 中并沒有透?jìng)?transclude)這樣的東西,但它有一個(gè)非常相似的概念,叫做內(nèi)容投影。 ?UpgradeModule ?也能讓這兩個(gè)特性實(shí)現(xiàn)互操作。

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

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

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

{{hero.name}}

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

當(dāng)從 AngularJS 中使用該組件時(shí),你可以為它提供內(nèi)容。正如它們將在 AngularJS 中被透?jìng)饕粯樱?nbsp;它們也在 Angular 中被投影到了 ?? 標(biāo)簽所在的位置:

{{mainCtrl.hero.description}}

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

把 Angular 的內(nèi)容透?jìng)鬟M(jìn) AngularJS 的組件型指令

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

如果一個(gè) AngularJS 組件型指令支持透?jìng)?,它就?huì)在自己的模板中使用 ?ng-transclude? 指令標(biāo)記出透?jìng)鞯降奈恢茫?/p>

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

{{$ctrl.hero.name}}

`, transclude: true };

如果你升級(jí)這個(gè)組件,并把它用在 Angular 中,你就能把準(zhǔn)備透?jìng)鞯膬?nèi)容放進(jìn)這個(gè)組件的標(biāo)簽中。

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

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

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

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

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

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

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

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

注意:這個(gè)工廠中的字符串 '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']
};

然后,你就可以把這個(gè)服務(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]; } }

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

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

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

例如,你可能有一個(gè) 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 ?列表中,以注冊(cè)它。

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ù),并把這個(gè)工廠注冊(cè)進(jìn) 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)用時(shí),你需要確保只在必要的時(shí)候才加載所需的資源,無論是加載靜態(tài)資產(chǎn)(Asset)還是代碼。要確保任何事都盡量推遲到必要時(shí)才去做,以便讓應(yīng)用更高效的運(yùn)行。當(dāng)要在同一個(gè)應(yīng)用中運(yùn)行不同的框架時(shí),更是如此。

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

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

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

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

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

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

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

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

在 Angular 的版本 8 中,惰性加載代碼只需使用動(dòng)態(tài)導(dǎo)入語(yǔ)法 ?import('...')? 即可。在這個(gè)應(yīng)用中,你創(chuàng)建了一個(gè)新服務(wù),它使用動(dòng)態(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)用。這會(huì)減少應(yīng)用初始包的大小,因?yàn)槟闵形醇虞d用戶目前不需要的代碼。你還要提供一種方法,在加載完畢后手動(dòng)啟動(dòng)它。AngularJS 提供了一種使用 angular.bootstrap() 方法并傳入一個(gè) HTML 元素來手動(dòng)引導(dǎo)應(yīng)用的方法。你的 AngularJS 應(yīng)用也應(yīng)該公開一個(gè)用來引導(dǎo) AngularJS 應(yīng)用的 ?bootstrap ?方法。

要確保 AngularJS 應(yīng)用中的任何清理工作都觸發(fā)過(比如移除全局監(jiān)聽器),你還可以實(shí)現(xiàn)一個(gè)方法來調(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 路由器會(huì)處理應(yīng)用中其余的路由。你的 Angular 應(yīng)用中會(huì)調(diào)用公開的 ?bootstrap ?方法,讓它在加載完發(fā)布包之后引導(dǎo) AngularJS 應(yīng)用。

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

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

在 Angular 應(yīng)用中,你需要一個(gè)組件作為 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(); } }

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

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

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

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

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

下列代碼往你的路由配置中添加了一個(gè)路由對(duì)象,其 ?matcher ?屬性是這個(gè)自定義匹配器,而 ?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 { }

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

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

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

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

要使用 ?LocationUpgradeModule?,就會(huì)從 ?@angular/common/upgrade? 中導(dǎo)入此符號(hà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()? 方法接受一個(gè)配置對(duì)象,該對(duì)象的 ?useHash ?為 ?LocationStrategy?,?hashPrefix ?為 URL 前綴。

?useHash ?屬性默認(rèn)為 ?false?,而 ?hashPrefix ?默認(rèn)為空 ?string?。傳遞配置對(duì)象可以覆蓋默認(rèn)值。

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

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

要想在 AngularJS 中使用 ?$location? 服務(wù)作為提供者,你需要使用一個(gè)工廠提供者來降級(jí) ?$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)航了,同時(shí),你仍然可以通過 AngularJS 和 Angular 進(jìn)行導(dǎo)航。

PhoneCat 升級(jí)教程

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

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

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

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

這確實(shí)是一個(gè)很好地起點(diǎn)。這些代碼使用了 AngularJS 1.5 的組件 API,并遵循了 AngularJS 風(fēng)格指南進(jìn)行組織, 在成功升級(jí)之前,這是一個(gè)很重要的準(zhǔn)備步驟。

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

切換到 TypeScript

因?yàn)槟銓⑹褂?nbsp;TypeScript 編寫 Angular 的代碼,所以在開始升級(jí)之前,先要把 TypeScript 的編譯器設(shè)置好。

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

先把 TypeScript 包安裝到項(xiàng)目中。

npm i typescript --save-dev

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

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

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)該要往項(xiàng)目目錄下添加一個(gè) ?tsconfig.json? 文件,?tsconfig.json? 文件會(huì)告訴 TypeScript 編譯器如何把 TypeScript 文件轉(zhuǎn)成 ES5 代碼,并打包進(jìn) CommonJS 模塊中。

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

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

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

npm run tsc:w

讓這個(gè)進(jìn)程一直在后臺(tái)運(yùn)行,監(jiān)聽任何變化并自動(dòng)重新編譯。

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

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

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

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

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

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

在 ?Phone ?服務(wù)中,你可以明確的把 ?$resource?&nb
名稱欄目:創(chuàng)新互聯(lián)Angular教程:Angular升級(jí)說明
網(wǎng)站網(wǎng)址:http://www.5511xx.com/article/djgeojo.html