第一節(jié):初識(shí)Angular-CLI
第二節(jié):登錄組件的構(gòu)建
第三節(jié):建立一個(gè)待辦事項(xiàng)應(yīng)用
第四節(jié):進(jìn)化!模塊化你的應(yīng)用
第五節(jié):多用戶版本的待辦事項(xiàng)應(yīng)用
第六節(jié):使用第三方樣式庫(kù)及模塊優(yōu)化用
第七節(jié):給組件帶來(lái)活力
Rx--隱藏在Angular 2.x中利劍
Redux你的Angular 2應(yīng)用
第八節(jié):查缺補(bǔ)漏大合集(上)
第九節(jié):查缺補(bǔ)漏大合集(下)
第八章:查缺補(bǔ)漏大合集(上)
這一章其實(shí)是我在前七章讀者評(píng)論和私信交流時(shí)發(fā)現(xiàn)很多點(diǎn)我是要么漏掉了紧唱,要么自己理解有誤兜喻。那這第八和第九章就來(lái)做一個(gè)小總結(jié)吧碉钠。本章我們討論如何在Angular2中引入第三方JS庫(kù)甫贯、惰性加載路由和子路由。
第三方JS類庫(kù)的引入
這個(gè)是許多人的困惑赏表,我們?cè)贏ngular2中使用了TypeScript,但大量的類庫(kù)是沒(méi)有TypeScript怎么辦匈仗?其實(shí)不用擔(dān)心瓢剿,非常簡(jiǎn)單。但在講方法前悠轩,我們最好還是理解一下背景间狂。
為什么JS不能直接使用
由于TypeScript是一個(gè)強(qiáng)類型語(yǔ)言,所以對(duì)于第三方類庫(kù)火架,我們需要知道它們的JavaScript里面的暴露給外部使用的這些對(duì)象和方法的類型定義是什么鉴象。
這個(gè)類型定義文件長(zhǎng)什么樣呢?我們來(lái)看一看何鸡,你可以進(jìn)入工程下的node_modules中的 @angular/common/src/directives/ng_class.d.ts
:
import { DoCheck, ElementRef, IterableDiffers, KeyValueDiffers, Renderer } from '@angular/core';
/**
* @ngModule CommonModule
*
* @whatItDoes Adds and removes CSS classes on an HTML element.
*
* @description
*
* The CSS classes are updated as follows, depending on the type of the expression evaluation:
* - `string` - the CSS classes listed in the string (space delimited) are added,
* - `Array` - the CSS classes declared as Array elements are added,
* - `Object` - keys are CSS classes that get added when the expression given in the value
* evaluates to a truthy value, otherwise they are removed.
*
* @stable
*/
export declare class NgClass implements DoCheck {
private _iterableDiffers;
private _keyValueDiffers;
private _ngEl;
private _renderer;
private _iterableDiffer;
private _keyValueDiffer;
private _initialClasses;
private _rawClass;
constructor(_iterableDiffers: IterableDiffers, _keyValueDiffers: KeyValueDiffers, _ngEl: ElementRef, _renderer: Renderer);
klass: string;
ngClass: string | string[] | Set<string> | {
[klass: string]: any;
};
ngDoCheck(): void;
private _cleanupClasses(rawClassVal);
private _applyKeyValueChanges(changes);
private _applyIterableChanges(changes);
private _applyInitialClasses(isCleanup);
private _applyClasses(rawClassVal, isCleanup);
private _toggleClass(klass, enabled);
}
可以看到這個(gè)文件其實(shí)就是用來(lái)做類型定義聲明的纺弊,我們一般把這種以 .d.ts 后綴結(jié)尾的文件叫做類型定義文件(Type Definition)。有了這個(gè)聲明定義骡男,我們就可以在TypeScript中使用了淆游。這個(gè)文件看起來(lái)也挺麻煩的,事實(shí)上真正需要你自己動(dòng)手寫(xiě)的類庫(kù)很少隔盛。我們來(lái)看一下一般的集成第三方類庫(kù)的過(guò)程是什么樣子的犹菱。
標(biāo)準(zhǔn)的JS庫(kù)引入方法
我們拿百度的echarts (https://github.com/ecomfe/echarts)圖表類庫(kù)的集成來(lái)說(shuō)明一下。我們先安裝其npm包骚亿,在命令行窗口輸入 npm install --save echarts
已亥,然后我們安裝其類型定義文件,在命令行窗口輸入 npm install --save-dev @types/echarts
来屠。然后。。就沒(méi)有然后了俱笛。這么簡(jiǎn)單嗎捆姜?是滴。
注意兩件事迎膜,首先我們安裝時(shí)使用了 --save-dev
開(kāi)關(guān)泥技,因?yàn)檫@個(gè)類型定義文件只對(duì)開(kāi)發(fā)時(shí)有用,它并不是我們工程的依賴磕仅,只是為了編寫(xiě)時(shí)的方便珊豹。
第二件事我們使用了 @types/echarts
這樣一個(gè)有點(diǎn)怪的名稱,其實(shí)是這樣的榕订,微軟維護(hù)了一個(gè)海量的類型定義數(shù)據(jù)中心店茶,這個(gè)就是 @types
。那么我們?yōu)榱藢ふ襡charts就會(huì)在 @types
這個(gè)目錄下搜索它的二級(jí)目錄劫恒。
這樣安裝之后贩幻,你可以在本地工程目錄下的 node_modules/@types/echarts/index.d.ts
找到echarts的定義:
// Type definitions for echarts
// Project: http://echarts.baidu.com/
// Definitions by: Xie Jingyang <https://github.com/xieisabug>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
declare namespace ECharts {
function init(dom:HTMLDivElement|HTMLCanvasElement, theme?:Object|string, opts?:{
devicePixelRatio?: number
renderer?: string
}):ECharts;
…//此處省略大部分聲明,可以查閱本地文件
}
declare module "echarts" {
export = ECharts;
}
一般情況下两嘴,到這步就結(jié)束了丛楚,此時(shí)我們可以試驗(yàn)一下是否可以使用了,在一個(gè)組件文件中嘗試引入echarts憔辫,如果你看到了智能提示中有你希望引入的類庫(kù)中的方法或?qū)ο笕ば蔷鸵磺许樌酉聛?lái)就可以正常使用這個(gè)類庫(kù)了贰您。

引入庫(kù)的特殊情況
但有的時(shí)候喧务,我們執(zhí)行第二步 npm install --save-dev @types/echarts
時(shí),會(huì)發(fā)現(xiàn)沒(méi)有找到對(duì)應(yīng)的類型定義文件枉圃。這個(gè)時(shí)候怎么辦呢功茴?
這時(shí)候要分兩種情況看,首先應(yīng)該去檢查一下node_modules目錄中的你要使用的類庫(kù)子目錄(本例中是echarts)中是否有類型定義文件孽亲,因?yàn)橛械念悗?kù)會(huì)把類型定義文件直接打包在npm的包中坎穿。比如我們前幾章接觸的angular-uuid,這個(gè)類庫(kù)其實(shí)就是直接把類型定義文件打包在npm package中的返劲×崦粒看下圖,如果是這種情況篮绿,那么我們什么都不需要做孵延,直接使用就好了。

當(dāng)然還有一種情形就是亲配,這樣也找不到尘应,或者這個(gè)類庫(kù)是我們的團(tuán)隊(duì)已有的惶凝、自己寫(xiě)的等等情況。這時(shí)候就得自己寫(xiě)一下犬钢,也很簡(jiǎn)單苍鲜,在 src/typings.d.ts
中加上一行:
declare module 'echarts';
然后在要使用此類庫(kù)的組件中引入:
import * as echarts from 'echarts';
后面就可以正常使用了,當(dāng)然這種添加方式是沒(méi)有智能提示和自動(dòng)完成的玷犹,你需要自己保證調(diào)用的正確性混滔。如果覺(jué)得不爽,還是希望有提示歹颓、類型檢查等等坯屿,那就得自己寫(xiě)一個(gè)類型定義文件了,可以參考 https://basarat.gitbooks.io/typescript/content/docs/types/ambient/d.ts.html 去編寫(xiě)自己的類型定義文件巍扛。
惰性路由和子路由
惰性路由
在需求和功能不斷添加和修改之后领跛,應(yīng)用的尺寸將會(huì)變得更大。在某一個(gè)時(shí)間點(diǎn)电湘,我們將達(dá)到一個(gè)頂點(diǎn)隔节,應(yīng)用 將會(huì)需要過(guò)多的時(shí)間來(lái)加載。這會(huì)帶來(lái)一定的性能問(wèn)題寂呛。
如何才能解決這個(gè)問(wèn)題呢怎诫?Angular2引進(jìn)了異步路由,我們可以惰性加載指定的模塊或組件贷痪。這樣給我們帶來(lái)了下列好處:
- 可以繼續(xù)開(kāi)發(fā)我們的新功能幻妓,但不再增加初始加載文件的大小。
- 只有在用戶請(qǐng)求時(shí)才加載特定模塊劫拢。
- 為那些只訪問(wèn)應(yīng)用程序某些區(qū)域的用戶加快加載速度
還是我們一起打造一個(gè)例子說(shuō)明一下肉津,之后大家就可以清楚的理解這個(gè)概念了。我們新建一個(gè)叫Playground的module舱沧。打開(kāi)一個(gè)命令行窗口妹沙,輸入 ng g m playgorund
,這樣Angular CLI非常聰明的幫我們建立了PlaygroundModule熟吏,不光如此距糖,它還幫我們建立了一個(gè)PlaygroundComponent。因?yàn)橐话銇?lái)說(shuō)牵寺,我們新建一個(gè)模塊肯定會(huì)至少有一個(gè)組件的悍引。
由于要做惰性加載,我們并不需要在根模塊AppModule中引入這個(gè)模塊帽氓,所以我們檢查一下根模塊 src/app/app.module.ts
中是否引入了PlaygroundModule趣斤,如果有,請(qǐng)去掉黎休。
首先為PlaygroundModule建立自己模塊的路由浓领,我們?nèi)绻袷谿oogle的代碼風(fēng)格建議的話玉凯,那么就應(yīng)該為每個(gè)模塊建立獨(dú)立的路由文件。
const routes: Routes = [
{ path: '', component: PlaygroundComponent },
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ],
})
export class PlaygroundRoutingModule { }
在src/app/app-routing.module.ts中我們要添加一個(gè)惰性路由指向PlaygroundModule
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { AuthGuardService } from './core/auth-guard.service';
const routes: Routes = [
{
path: '',
redirectTo: 'login',
pathMatch: 'full'
},
…
{
path: 'playground',
loadChildren: 'app/playground/playground.module#PlaygroundModule',
}
];
@NgModule({
imports: [
RouterModule.forRoot(routes)
],
exports: [
RouterModule
]
})
export class AppRoutingModule {}
在這段代碼中我們看到一個(gè)新面孔镊逝,loadChildren
壮啊。路由器用 loadChildren
屬性來(lái)映射我們希望惰性加載的模塊文件嫉鲸,這里是 PlaygroundModule
撑蒜。路由器將接收我們的 loadChildren
字符串,并把它動(dòng)態(tài)加載進(jìn) PlaygroundModule
玄渗,它的路由被動(dòng)態(tài)合并到我們的配置中座菠,然后加載所請(qǐng)求的路由。但只有在首次加載該路由時(shí)才會(huì)這樣做藤树,后續(xù)的請(qǐng)求都會(huì)立即完成浴滴。
app/playground/playground.module#PlaygroundModule
這個(gè)表達(dá)式是這樣的規(guī)則:模塊的路徑#模塊名稱
現(xiàn)在我們回顧一下,在應(yīng)用啟動(dòng)時(shí)岁钓,我們并沒(méi)有加載PlaygroundModule升略,因?yàn)樵贏ppModule中沒(méi)有它的引用。但是當(dāng)你在瀏覽器中手動(dòng)輸入 http://localhost:4200/playground
時(shí)屡限,系統(tǒng)在此時(shí)加載 PlaygroundModule
品嚣。
子路由
程序復(fù)雜了之后,一層的路由可能就不會(huì)夠用了钧大,在一個(gè)模塊內(nèi)部由于功能較復(fù)雜翰撑,需要再劃分出二級(jí)甚至更多級(jí)別的路徑。這種情況下我們就需要Angular2提供的一個(gè)內(nèi)建功能叫做:子路由啊央。
我們向來(lái)認(rèn)為例子是最好的說(shuō)明眶诈,所以還是來(lái)做一個(gè)小功能:現(xiàn)在我們需要對(duì)一個(gè)叫playground的路徑下添加子路由,子路由有2個(gè):one和two瓜饥。其中one下面還有一層路徑叫three逝撬。形象的表示一下,就像下面的結(jié)構(gòu)一樣乓土。
/playground---|
|/one
|--------|three
|/two
那么我們還是先在項(xiàng)目工程目錄輸入 ng g c playground/one
宪潮,然后再執(zhí)行 ng g c playground/two
,還有一個(gè)three帐我,所以再來(lái):ng g c playground/three
坎炼。
現(xiàn)在我們有了三個(gè)組件,看看怎么處理路由吧拦键,原有的模塊路由文件如下:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PlaygroundComponent } from './playground.component';
const routes: Routes = [
{
path: '',
component: PlaygroundComponent
},
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ],
})
export class PlaygroundRoutingModule { }
我們首先需要在模塊的根路由下添加one和two谣光,Angular2在路由定義數(shù)組中對(duì)于每個(gè)路由定義對(duì)象都有一個(gè)屬性叫做children,這里就是指定子路由的地方了芬为。所以在下面代碼中我們把one和two都放入了children數(shù)組中萄金。
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PlaygroundComponent } from './playground.component';
import { OneComponent } from './one/one.component';
import { TwoComponent } from './two/two.component';
const routes: Routes = [
{
path: '',
component: PlaygroundComponent,
children: [
{
path: 'one',
component: OneComponent,
},
{
path: 'two',
component: TwoComponent
}
]
},
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ],
})
export class PlaygroundRoutingModule { }
這只是定義了路由數(shù)據(jù)蟀悦,我們還需要在某個(gè)地方顯示路由指向的組件,那么這里面我們還是在PlaygroundComponent的模版中把路由插座放入吧氧敢。
<ul>
<li><a routerLink="one">One</a></li>
<li><a routerLink="two">Two</a></li>
</ul>
<router-outlet></router-outlet>
現(xiàn)在我們?cè)囼?yàn)一下日戈,打開(kāi)瀏覽器輸入 http://localhost:4200/playground
我們看到兩個(gè)鏈接,你可以分別點(diǎn)一下孙乖,觀察地址欄浙炼。應(yīng)該可以看到,點(diǎn)擊one時(shí)唯袄,地址變成 http://localhost:4200/playground/one
在我們放置路由插座的位置也會(huì)出現(xiàn)one works弯屈。當(dāng)然點(diǎn)擊two時(shí)也會(huì)有對(duì)應(yīng)的改變。這說(shuō)明我們的子路由配置好用了恋拷!

當(dāng)然有的時(shí)候還需要更深的層級(jí)的子路由资厉,其實(shí)也很簡(jiǎn)單。就是重復(fù)我們剛才做的就好蔬顾,只不過(guò)要在對(duì)應(yīng)的子路由節(jié)點(diǎn)上宴偿。下面我們還是演練一下,在點(diǎn)擊one之后我們希望到達(dá)一個(gè)有子路由的頁(yè)面(也就是子路由的子路由)诀豁。于是我們?cè)贠neComponent節(jié)點(diǎn)下又加了children窄刘,然后把ThreeComponent和對(duì)應(yīng)的路徑寫(xiě)入
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { PlaygroundComponent } from './playground.component';
import { OneComponent } from './one/one.component';
import { TwoComponent } from './two/two.component';
import { ThreeComponent } from './three/three.component';
const routes: Routes = [
{
path: '',
component: PlaygroundComponent,
children: [
{
path: 'one',
component: OneComponent,
children: [
{
path: 'three',
component: ThreeComponent
}
]
},
{
path: 'two',
component: TwoComponent
}
]
},
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ],
})
export class PlaygroundRoutingModule { }
當(dāng)然,還是一樣且叁,我們需要改造一下OneComponent的模版以便于它可以顯示子路由的內(nèi)容都哭。改動(dòng) src/app/playground/one/one.component.html
為如下內(nèi)容
<p>
one works!
</p>
<ul>
<li><a routerLink="three">Three</a></li>
</ul>
<router-outlet></router-outlet>
這回我們看到如果在瀏覽器中輸入 http://localhost:4200/playground/one/three
會(huì)看到如圖所示的結(jié)果:

經(jīng)過(guò)這個(gè)小練習(xí),相信再?gòu)?fù)雜的路由你也可以搞定了逞带。但是我要說(shuō)一句欺矫,個(gè)人不是很推薦過(guò)于復(fù)雜的路由(復(fù)雜這里指層級(jí)嵌套太多)。層級(jí)多了之后意味著這個(gè)模塊太大了展氓,負(fù)責(zé)了過(guò)多它不應(yīng)該負(fù)責(zé)的事情穆趴。也就是說(shuō)當(dāng)要使用子路由時(shí),一定多問(wèn)自己幾遍遇汞,這樣做是必須的嗎未妹?可以用別的方式解決嗎?是不是我的模塊改拆分了空入?
本章代碼: https://github.com/wpcfan/awesome-tutorials/tree/master/angular2/ng2-tut
慕課網(wǎng) Angular 視頻課上線: http://coding.imooc.com/class/123.html?mc_marking=1fdb7649e8a8143e8b81e221f9621c4a&mc_channel=banner
紙書(shū)出版了络它,比網(wǎng)上內(nèi)容豐富充實(shí)了,歡迎大家訂購(gòu)歪赢!
京東鏈接:https://item.m.jd.com/product/12059091.html?from=singlemessage&isappinstalled=0

第一節(jié):初識(shí)Angular-CLI
第二節(jié):登錄組件的構(gòu)建
第三節(jié):建立一個(gè)待辦事項(xiàng)應(yīng)用
第四節(jié):進(jìn)化化戳!模塊化你的應(yīng)用
第五節(jié):多用戶版本的待辦事項(xiàng)應(yīng)用
第六節(jié):使用第三方樣式庫(kù)及模塊優(yōu)化用
第七節(jié):給組件帶來(lái)活力
Rx--隱藏在Angular 2.x中利劍
Redux你的Angular 2應(yīng)用
第八節(jié):查缺補(bǔ)漏大合集(上)
第九節(jié):查缺補(bǔ)漏大合集(下)