Angular 從0到1 (八):史上最簡單的 Angular 教程

第一節:初識Angular-CLI
第二節:登陸組件的構建
第三節:創建一個待辦事項應用
第四節:進化!模塊化你的應用
第五節:多用戶版本的待辦事項應用
第六節:使用第三方樣式庫及模塊優化用
第七節:給組件帶來活力
Rx--隱藏在 Angular 中的利劍
Redux 你的 Angular 應用
第八節:查缺補漏大合集(上)
第九節:查缺補漏大合集(下)javascript

第八章:查缺補漏大合集(上)

這一章實際上是我在前七章讀者評論和私信交流時發現不少點我是要麼漏掉了,要麼本身理解有誤。那這第八和第九章就來作一個小總結吧。本章咱們討論如何在Angular2中引入第三方JS庫、惰性加載路由和子路由。html

第三方JS類庫的引入

這個是許多人的困惑,咱們在Angular2中使用了TypeScript,但大量的類庫是沒有TypeScript怎麼辦?其實不用擔憂,很是簡單。但在講方法前,咱們最好仍是理解一下背景。java

爲何JS不能直接使用

因爲TypeScript是一個強類型語言,因此對於第三方類庫,咱們須要知道它們的JavaScript裏面的暴露給外部使用的這些對象和方法的類型定義是什麼。node

這個類型定義文件長什麼樣呢?咱們來看一看,你能夠進入工程下的node_modules中的 @angular/common/src/directives/ng_class.d.ts:git

import { DoCheck, ElementRef, IterableDiffers, KeyValueDiffers, Renderer } from '@angular/core';

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);
}複製代碼

能夠看到這個文件其實就是用來作類型定義聲明的,咱們通常把這種以 .d.ts 後綴結尾的文件叫作類型定義文件(Type Definition)。有了這個聲明定義,咱們就能夠在TypeScript中使用了。這個文件看起來也挺麻煩的,事實上真正須要你本身動手寫的類庫不多。咱們來看一下通常的集成第三方類庫的過程是什麼樣子的。github

標準的JS庫引入方法

咱們拿百度的echarts (github.com/ecomfe/echa… npm install --save echarts ,而後咱們安裝其類型定義文件,在命令行窗口輸入 npm install --save-dev @types/echarts 。而後。。就沒有而後了。這麼簡單嗎?是滴。typescript

注意兩件事,首先咱們安裝時使用了 --save-dev 開關,由於這個類型定義文件只對開發時有用,它並非咱們工程的依賴,只是爲了編寫時的方便。
第二件事咱們使用了 @types/echarts 這樣一個有點怪的名稱,實際上是這樣的,微軟維護了一個海量的類型定義數據中心,這個就是 @types。那麼咱們爲了尋找echarts就會在 @types 這個目錄下搜索它的二級目錄。npm

這樣安裝以後,你能夠在本地工程目錄下的 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;
}複製代碼

通常狀況下,到這步就結束了,此時咱們能夠試驗一下是否可使用了,在一個組件文件中嘗試引入echarts,若是你看到了智能提示中有你但願引入的類庫中的方法或對象,那就一切順利,接下來就能夠正常使用這個類庫了。瀏覽器

引入echarts看到智能提示

引入庫的特殊狀況

但有的時候,咱們執行第二步 npm install --save-dev @types/echarts 時,會發現沒有找到對應的類型定義文件。這個時候怎麼辦呢?
這時候要分兩種狀況看,首先應該去檢查一下node_modules目錄中的你要使用的類庫子目錄(本例中是echarts)中是否有類型定義文件,由於有的類庫會把類型定義文件直接打包在npm的包中。好比咱們前幾章接觸的angular-uuid,這個類庫其實就是直接把類型定義文件打包在npm package中的。看下圖,若是是這種狀況,那麼咱們什麼都不須要作,直接使用就行了。

有的類庫直接將類型定義打包在npm中

固然還有一種情形就是,這樣也找不到,或者這個類庫是咱們的團隊已有的、本身寫的等等狀況。這時候就得本身寫一下,也很簡單,在 src/typings.d.ts 中加上一行:

declare module 'echarts';複製代碼

而後在要使用此類庫的組件中引入:

import * as echarts from 'echarts';複製代碼

後面就能夠正常使用了,固然這種添加方式是沒有智能提示和自動完成的,你須要本身保證調用的正確性。若是以爲不爽,仍是但願有提示、類型檢查等等,那就得本身寫一個類型定義文件了,能夠參考 basarat.gitbooks.io/typescript/… 去編寫本身的類型定義文件。

惰性路由和子路由

惰性路由

在需求和功能不斷添加和修改以後,應用的尺寸將會變得更大。在某一個時間點,咱們將達到一個頂點,應用 將會須要過多的時間來加載。這會帶來必定的性能問題。
如何才能解決這個問題呢?Angular2引進了異步路由,咱們能夠惰性加載指定的模塊或組件。這樣給咱們帶來了下列好處:

  • 能夠繼續開發咱們的新功能,但再也不增長初始加載文件的大小。
  • 只有在用戶請求時才加載特定模塊。
  • 爲那些只訪問應用程序某些區域的用戶加快加載速度

仍是咱們一塊兒打造一個例子說明一下,以後你們就能夠清楚的理解這個概念了。咱們新建一個叫Playground的module。打開一個命令行窗口,輸入 ng g m playgorund ,這樣Angular CLI很是聰明的幫咱們創建了PlaygroundModule,不光如此,它還幫咱們創建了一個PlaygroundComponent。由於通常來講,咱們新建一個模塊確定會至少有一個組件的。
因爲要作惰性加載,咱們並不須要在根模塊AppModule中引入這個模塊,因此咱們檢查一下根模塊 src/app/app.module.ts 中是否引入了PlaygroundModule,若是有,請去掉。
首先爲PlaygroundModule創建本身模塊的路由,咱們若是遵照Google的代碼風格建議的話,那麼就應該爲每一個模塊創建獨立的路由文件。

const routes: Routes = [
  { path: '', component: PlaygroundComponent },
];

@NgModule({
  imports: [ RouterModule.forChild(routes) ],
  exports: [ RouterModule ],
})
export class PlaygroundRoutingModule { }複製代碼

在src/app/app-routing.module.ts中咱們要添加一個惰性路由指向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 {}複製代碼

在這段代碼中咱們看到一個新面孔,loadChildren 。路由器用 loadChildren 屬性來映射咱們但願惰性加載的模塊文件,這裏是 PlaygroundModule 。路由器將接收咱們的 loadChildren 字符串,並把它動態加載進 PlaygroundModule ,它的路由被動態合併到咱們的配置中,而後加載所請求的路由。但只有在首次加載該路由時纔會這樣作,後續的請求都會當即完成。
app/playground/playground.module#PlaygroundModule 這個表達式是這樣的規則:模塊的路徑#模塊名稱
如今咱們回顧一下,在應用啓動時,咱們並無加載PlaygroundModule,由於在AppModule中沒有它的引用。可是當你在瀏覽器中手動輸入 http://localhost:4200/playground 時,系統在此時加載 PlaygroundModule

子路由

程序複雜了以後,一層的路由可能就不會夠用了,在一個模塊內部因爲功能較複雜,須要再劃分出二級甚至更多級別的路徑。這種狀況下咱們就須要Angular2提供的一個內建功能叫作:子路由。
咱們向來認爲例子是最好的說明,因此仍是來作一個小功能:如今咱們須要對一個叫playground的路徑下添加子路由,子路由有2個:one和two。其中one下面還有一層路徑叫three。形象的表示一下,就像下面的結構同樣。

/playground---|
              |/one
              |--------|three
              |/two複製代碼

那麼咱們仍是先在項目工程目錄輸入 ng g c playground/one,而後再執行 ng g c playground/two ,還有一個three,因此再來:ng g c playground/three
如今咱們有了三個組件,看看怎麼處理路由吧,原有的模塊路由文件以下:

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在路由定義數組中對於每一個路由定義對象都有一個屬性叫作children,這裏就是指定子路由的地方了。因此在下面代碼中咱們把one和two都放入了children數組中。

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 { }複製代碼

這只是定義了路由數據,咱們還須要在某個地方顯示路由指向的組件,那麼這裏面咱們仍是在PlaygroundComponent的模版中把路由插座放入吧。

<ul>
  <li><a routerLink="one">One</a></li>
  <li><a routerLink="two">Two</a></li>
</ul>

<router-outlet></router-outlet>複製代碼

如今咱們試驗一下,打開瀏覽器輸入 http://localhost:4200/playground 咱們看到兩個連接,你能夠分別點一下,觀察地址欄。應該能夠看到,點擊one時,地址變成 http://localhost:4200/playground/one 在咱們放置路由插座的位置也會出現one works。固然點擊two時也會有對應的改變。這說明咱們的子路由配置好用了!

子路由的小例子

固然有的時候還須要更深的層級的子路由,其實也很簡單。就是重複咱們剛纔作的就好,只不過要在對應的子路由節點上。下面咱們仍是演練一下,在點擊one以後咱們但願到達一個有子路由的頁面(也就是子路由的子路由)。因而咱們在OneComponent節點下又加了children,而後把ThreeComponent和對應的路徑寫入

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 { }複製代碼

固然,仍是同樣,咱們須要改造一下OneComponent的模版以便於它能夠顯示子路由的內容。改動 src/app/playground/one/one.component.html 爲以下內容

<p>
  one works!
</p>
<ul>
  <li><a routerLink="three">Three</a></li>
</ul>
<router-outlet></router-outlet>複製代碼

這回咱們看到若是在瀏覽器中輸入 http://localhost:4200/playground/one/three 會看到如圖所示的結果:

更多層級的子路由

通過這個小練習,相信再複雜的路由你也能夠搞定了。可是我要說一句,我的不是很推薦過於複雜的路由(複雜這裏指層級嵌套太多)。層級多了以後意味着這個模塊太大了,負責了過多它不該該負責的事情。也就是說當要使用子路由時,必定多問本身幾遍,這樣作是必須的嗎?能夠用別的方式解決嗎?是否是個人模塊改拆分了?

本章代碼: github.com/wpcfan/awes…

紙書出版了,比網上內容豐富充實了,歡迎你們訂購!
京東連接:item.m.jd.com/product/120…

Angular從零到一
相關文章
相關標籤/搜索