AngularJS學習之四(分支之二):架構概述

架構概述

Angular是一個用於在HTML以及JavaScript或相似於編譯爲JavaScript的TypeScript語言中構建客戶端應用程序的框架。html


框架由幾個庫組成,其中一些是核心的,一些是可選的。java


你經過編寫HTML模板(帶有Angular化的標籤),編寫組件類來管理這些模板,在services中添加應用程序邏輯,並在模塊中組裝組件和services。程序員


而後,經過引導程序 root module來啓動程序。Angular接管,在瀏覽器中顯示您的應用內容,並根據您提供的指示響應用戶的互動。npm


固然,這裏有比已經提到的更多的內容。您將在隨後的頁面中瞭解那些詳細信息。如今,咱們先來關注大局。bootstrap

overview

這張架構圖標識了Angular應用程序的八個主要的構建塊:數組

學習這些構建塊,你正在正確的方向前進。瀏覽器

此頁面引用的代碼都有一個可用的 live example服務器

模塊

Component

Angular應用程序是模塊化的,Angular有一個名爲Angular modules或NgModules的模塊化系統。網絡

Angular模塊是一個大問題。本頁介紹模塊; 在Angular modules頁會深度地講解它們。

每一個Angular app具備至少一個Angular模塊類, the root module,傳統命名爲AppModule


雖然root module能夠是一個小應用程序的惟一模塊,大多數應用程序有更多的功能模塊,每一個內聚的代碼塊(模塊)專一於一個應用領域,一個工做流,或者是密切相關的功能集。

一個Angular模塊,不管是根仍是功能,都是一個帶着 @NgModule裝飾器的類。

裝飾器是修改JavaScript類的函數。Angular有許多裝飾器將元數據附加到類上,以便知道這些類的含義和它們應該如何工做。在Learn more(瞭解更多)能夠了解更多在網絡上的裝飾器的介紹。

NgModule 是一個裝飾器函數,它接受一個元數據對象,其屬性描述模塊。最重要的屬性是:

  • declarations(裝飾品)- 屬於這個模塊的視圖類。Angular有三種視圖類:組件指令管道

  • exports - declarations(聲明)的子集,在其餘模塊的組件模板中應該可見和可用。

  • imports -其餘模塊,須要導出類,被本模塊的組件模板聲明。

  • providers -本模塊services的提供者提供給全局services集合; 它們在應用程序的全部部分均可訪問。

  • bootstrap -主要應用視圖,稱爲根組件,承載全部其餘應用視圖。只有root module應設置這個bootstrap屬性。


這裏有一個簡單的根模塊:

app/app.module.ts

import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; @NgModule({ imports: [ BrowserModule ], providers: [ Logger ], declarations: [ AppComponent ], exports: [ AppComponent ], bootstrap: [ AppComponent ] }) export class AppModule { }

AppComponent(懷疑這裏寫錯了,應該是AppModule)的export只是說明如何導出; 在該示例中實際上不是必需的。一個root module沒有理由export任何東西,由於其餘組件不須要導入root module。

經過引導它的 root module來運行一個應用程序。在開發過程當中,你可能會像這樣的 main.ts 文件中引導AppModule。

app/main.ts

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { AppModule } from './app.module'; platformBrowserDynamic().bootstrapModule(AppModule);

Angular 模塊和JavaScript模塊

Angular模塊-一個裝飾着@NgModule的類-是Angular的基本特徵。

JavaScript還有本身的模塊系統,用於管理JavaScript對象的集合。 它徹底不一樣,與Angular模塊系統無關。

在JavaScript每一個文件是一個模塊,並在該文件中定義的全部對象屬於該模塊。該模塊聲明某些對象是經過export關鍵詞而公共化。其餘JavaScript模塊使用import語句從其餘模塊訪問公共對象。

import { NgModule } from '@angular/core'; import { AppComponent } from './app.component';
export class AppModule { }

能夠在網上去學習更多有關JavaScript的模塊系統的知識。

這是兩個不一樣和互補的模塊系統。同時使用它們來編寫您的應用程序。

Angular 庫


Component

Angular做爲JavaScript模塊的一個集合。(這裏有些歧義,本來是Angular ships as a collection of JavaScript modules,ships as在這個地方應該如何理解呢?這麼翻譯,顯得和上文不連貫)。你能夠把它們理解成爲一個庫模塊。


每個Angular的庫的名字都是以@angular這樣的前綴開始的。



您能夠用npm包管理器去安裝它們,而且用JavaScript的import語句引入它們。 

例如,從@angular/core庫中引入Angular Component 裝飾器就像這樣:

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

您還可使用JavaScript的import語句,從Angular庫導入Angular modules:

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

在上面這個簡單的 root module 例子裏面,應用程序模塊須要在 BrowserModule裏面的內容。 爲了能夠訪問,像這樣把它增長到@NgModule元數據中。

imports: [ BrowserModule ],

經過這樣作,你就能夠一塊兒使用Angular和JavaScript的模塊系統了。

由於這兩個系統共享了「imports」和「exports」這兩個詞彙,因此很容易把它們搞混。那就先放在那裏。隨着時間的推移和經驗的積累,這個混亂會搞清楚的。

在 Angular modules 頁能夠學到更多。

組件

Component

一個控制一片屏幕的組件稱爲視圖。

例如,下面的視圖是由組件來控制的。

  • 具備導航連接的 app root。
  • 英雄列表。
  • 英雄編輯器。

您定義了一個組件的應用程序邏輯 - 它支持視圖-在類內部。這個類和視圖經過屬性和方法的API進行交互。

例如,這個HeroListComponent有一個heroes屬性,用來返回它從一個服務中獲取的一個的英雄的數組。 HeroListComponent也有一個selectHero()方法設置selectedHero屬性,當用戶點擊選擇從該列表中選擇一個英雄。

app/hero-list.component.ts (class)

export class HeroListComponent implements OnInit { heroes: Hero[]; selectedHero: Hero; constructor(private service: HeroService) { } ngOnInit() { this.heroes = this.service.getHeroes(); } selectHero(hero: Hero) { this.selectedHero = hero; } }

Angular在用戶在應用程序中移動時(瀏覽時)建立,更新和銷燬組件。您的app能夠就像上面生命的ngOnInit() 那樣,經過可選的lifecycle hooks(生命週期的掛鉤)在每個時刻進行響應。

模板

Template


您定義一個組件視圖和它相伴隨的模板。一個模板是一種HTML形式,告訴Angular如何渲染組件。

一個模板看起來像常規HTML,除了一些差別。下面是針對咱們的HeroListComponent的一個模板HeroListComponent:



app/hero-list.component.html

   
  1. <h2>Hero List</h2>
  2. <p><i>Pick a hero from the list</i></p>
  3. <ul>
  4. <li *ngFor="let hero of heroes" (click)="selectHero(hero)">
  5. {{hero.name}}
  6. </li>
  7. </ul>
  8. <hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>
<h2>Hero List</h2><p><i>Pick a hero from the list</i></p><ul> <li *ngFor="let hero of heroes" (click)="selectHero(hero)"> {{hero.name}} </li></ul><hero-detail *ngIf="selectedHero" [hero]="selectedHero"></hero-detail>

雖然這個模板使用典型的HTML元素像<h2> 和 <p>,它也有一些不一樣。相似 *ngFor{{hero.name}}(click),[hero],和<hero-detail> 的代碼,使用的是 Angular's template syntax (Angular 模板語法)。

在模板的最後一行時,這個<hero-detail>標籤是表示一個新組件的自定義元件,HeroDetailComponent。

該HeroDetailComponent是一個不一樣於你一直在看的HeroListComponent的組件。該HeroDetailComponent(代碼沒有顯示)呈現一個特定的英雄的事實,即用戶從所呈現的列表HeroListComponent中選擇英雄。該HeroDetailComponent是HeroListComponent的孩子。

Metadata


請注意如何<hero-detail>輕鬆地位於原生的HTML元素中。自定義組件與原生HTML在同一佈局中無縫組合。





元數據

Metadata

元數據告訴 Angular 如何去處理一個類。


Looking back at the code (沿着代碼往回看)找到HeroListComponent,你能夠看到它僅僅是一個類。看不到任何框架的證據,裏面也根本沒有「Angular」。

事實上, HeroListComponent 真的只是一個類。 在你把它告訴Angular以前,它並非一個組件。

想要告訴AngularHeroListComponent 是一個組件,那就給這個類附屬上元數據。

在 TypeScript中,你使用一個decorator(裝飾器)關鍵字去附屬上元數據。這裏有一些關於 HeroListComponent的元數據:

app/hero-list.component.ts (metadata)

@Component({ moduleId: module.id, selector: 'hero-list', templateUrl: 'hero-list.component.html', providers: [ HeroService ] }) export class HeroListComponent implements OnInit { /* . . . */ }

這裏有一個 @Component 裝飾器,這會馬上把在它下面的這個類標識成一個組件類。,

這個@Component 裝飾器採用了一個配置對象,這個對象具備Angular所須要的去建立和表示組件和它的視圖的信息。 

這裏有一些可能的 @Component 配置選項:

  • moduleId:爲模塊相關的URL(相似templateUrl),設置基礎地址的源 (module.id) 。
  • selector:CSS 選擇器告訴Angular去建立和插入一個這個組件的實例到在當前的HMTL中找到一個<hero-list>標籤的地方。例如,若是一個app的HTML包含<hero-list></hero-list>,而後Angular在這些標籤中間插入一個HeroListComponent 視圖的實例。

  • templateUrl:這個模塊的HTML模板的模塊相關的地址,顯示在上面。

  • providers:爲服務提供組件所須要的依賴注入提供者的數組(array of dependency injection providers) 。這是一個辦法,能讓Angular告訴這個組件構造器須要一個 HeroService 這樣它能夠獲取英雄的列表去顯示。
Metadata

在 @Component 中的元數據告訴Angular哪裏去得到你爲組件指定的主要的構件塊。 

模板、元數據、和組件一塊兒描述了一個視圖。

應用其它元數據裝飾器也能夠相似地指導Angular的行爲。@Injectable、 @Input、和@Output 是一些最流行的裝飾器。


這個架構(原文The architectural takeaway,暫時不確認如何解釋) 就是你必須在你的代碼中增長元數據,這樣Angular才知道要作什麼。

數據綁定

沒有框架,你須要負責將數據值送入HTML控件中,而且把用戶地反應變成行爲和數據的更新。 手動編寫這樣的推/拉邏輯是使人乏味的,容易產生錯誤,是任何有經驗的jQuery程序員能夠做證的一場噩夢。

Data Binding

Angular支持數據綁定, 一種機制用於組件的部分和模板對應部分的協做。給模板HTML增長綁定標籤去告訴Angular如何去鏈接雙方。

就像圖例所示,有四種格式的數據綁定語法。每一種格式都有一個方向,面向DOM的,從DOM出來的,或者是雙方向的。



這個HeroListComponent example 模板有三種格式:

app/hero-list.component.html (binding)

<li>{{hero.name}}</li> <hero-detail [hero]="selectedHero"></hero-detail> <li (click)="selectHero(hero)"></li><li>{{hero.name}}</li> <hero-detail [hero]="selectedHero"></hero-detail> <li (click)="selectHero(hero)"></li>
  • 這個{{hero.name}} interpolation (插值)在<li>裏面顯示了組件的 hero.name 屬性值。

  • The [hero] property binding (屬性綁定)傳遞 selectedHero 的值從父親的 HeroListComponent到孩子的 HeroDetailComponenthero 屬性。

  • 這個(click) event binding (事件綁定)當用戶點擊一個英雄的名字的時候,調用組件的 selectHero 方法。

雙向數據綁定 是一個重要的第四種格式,使用ngModel 指示符,在單一的符號中捆綁着屬性和事件綁定。這裏有一個從  HeroDetailComponent 模板裏面找到的例子:

app/hero-detail.component.html (ngModel)

<input [(ngModel)]="hero.name"><input [(ngModel)]="hero.name">

在雙向綁定中,一個數據屬性值使用屬性綁定從組件流向輸入框。使用事件綁定,用戶的變化也反向流回組件,重設屬性的最後的值。

Angular 在一次JavaScript 事件循環中處理全部 數據綁定,從應用組件樹的根直到全部的孩子組件。 

Data Binding

數據綁定在一個模板和它的組件之間擔任着很重要的交流角色。


Parent/Child binding







指示符

Parent child

Angular模板是動態的。當Angular渲染它們的時候,它根據指示符的指導來改變DOM。

一個指示符是一個帶有一個@Directive 裝飾器的類。一個組件是一個 directive-with-a-template(指示符和一個模板); @Component 裝飾器其實是一個 @Directive 裝飾器擴展後,帶有面向模板的功能。

儘管一個組件技術上是一個指示符,但組件是這樣的清晰,位於Angular應用程序的中心,這個結構化的總覽仍是選擇將指示符和組件分開。

兩個其它類型的指示符也存在:structural 和 attribute 指示符。

它們嘗試去在一個元素標籤中顯示(就像attributes所作),有時是經過名字,可是更常見的是做爲一個分配或者綁定的目標。

Structural 指示符經過增長、移除和替換在DOM中的元素來改變佈局。

這個example template使用了兩個內建的structural指示符:

app/hero-list.component.html (structural)

<li *ngFor="let hero of heroes"></li> <hero-detail *ngIf="selectedHero"></hero-detail><li *ngFor="let hero of heroes"></li> <hero-detail *ngIf="selectedHero"></hero-detail>
  • *ngFor 告訴Angular爲每個在heroes 列表中的英雄生成一個 <li> 。
  • *ngIf 包含了 HeroDetail 組件,僅當一個選擇的英雄存在的時候。

Attribute指示符修改了一個存在的元素的顯示或者表現。在模板中,他們看起來像規則的HTML屬性,全部有這樣的名字。

這個ngModel 指示符,實現了雙向數據綁定,是一個attribute指示符的例子。 ngModel 經過設置它的顯示值屬性和對於變化事件的響應,修改了一個已經存在元素的表現 (典型的例如 <input>) 。

app/hero-detail.component.html (ngModel)

<input [(ngModel)]="hero.name"><input [(ngModel)]="hero.name">

Angular還有一些指示符或者改變了佈局結構 (例如,ngSwitch) 或者修改了DOM元素或組件的一些方面(例如, ngStyle 和ngClass).

固然,你也能夠編寫你本身的指示符。相似 HeroListComponent 的組件就是一種自定義指示符。

服務

Service

服務 是一個廣闊的分類,包含了任意的值,函數,或者你的應用須要的功能。

幾乎全部事物均可以是一個服務。一個服務典型上是一個類,帶着一個狹窄的定義好的目的。它將作指定的事情而且把它作好。


例子包括:

  • 日誌服務
  • 數據服務
  • 消息總線
  • 稅收計算器
  • 應用程序配置

Angular關於服務沒有什麼特別的內容。Angular沒有關於服務的定義。沒有服務基礎類,沒有地方去註冊服務。

可是服務是任何Angular程序的基礎。組件是服務的大的消費者。

這裏有一個關於服務的類的例子,輸出日誌到瀏覽器控制檯:

app/logger.service.ts (class)

export class Logger { log(msg: any) { console.log(msg); } error(msg: any) { console.error(msg); } warn(msg: any) { console.warn(msg); } }

這裏有一個 HeroService 獲取英雄而且把他們返回到一個解析了的 Promise。這個HeroService 依賴這個Logger 服務,同時另一個BackendService 處理着服務器通訊的工做。

app/hero.service.ts (class)

export class HeroService { private heroes: Hero[] = []; constructor( private backend: BackendService, private logger: Logger) { } getHeroes() { this.backend.getAll(Hero).then( (heroes: Hero[]) => { this.logger.log(`Fetched ${heroes.length} heroes.`); this.heroes.push(...heroes); // fill cache }); return this.heroes; } }

服務隨處都是。

組件類應該精簡。它們不從服務器端獲取數據,校驗用戶輸入,或者輸出日誌到控制檯。它們把這些工做委託給服務。

一個組件的工做僅僅是提供用戶體驗(注:這裏的用戶可能指的是程序員)。它做爲視圖(被模板所渲染)和應用邏輯(常常包含一個model的符號)的媒介。一個好的組件表明着對於數據綁定的屬性和方法。它把全部的別的重要的事情委託給服務。(注:本來 It delegates everything nontrivial to services.)

Angular並不強制要求實現這些原則。若是你用3000行寫了一個「kitchen sink」的組件,它也並不會抱怨。

Angular經過很容易地把你地應用邏輯做爲因素注入到服務中來幫助你遵循 這些原則,而且經過dependency injection 來使這些服務對於組件可用。

依賴注入

Service

Dependency injection(依賴注入) 是一種方法使用它須要的徹底的(fully-formed)依賴去支撐一個類的一個新的實例。大多數依賴是服務。Angular使用依賴注入去提供組件所須要的服務給新的組件。


Angular能夠經過看到構造器參數的類型能夠告訴一個組件須要哪些服務。例如,你的 HeroListComponent須要一個HeroService

app/hero-list.component.ts (constructor)

constructor(private service: HeroService) { }

當Angular建立一個組件時,它首先會爲組件須要的服務申請一個injector (注射器)。

一個注射器維持着它以前建立的服務實例的一個容器。若是一個請求的服務的實例不在容器中,注射器在返回服務給Angular以前會建立一個而且把它放入容器。當全部被請求的服務都被解析和返回,Angular會用這些服務做爲參數去呼叫組件構造器。這就是依賴注入

HeroService 注入的過程看起來有一點像這樣:

Service

若是這個注射器沒有一個 HeroService,它時怎麼知道去建立一個呢?

簡而言之,你必須在以前就用注射器註冊一個 HeroService 提供者(provider)。一個提供者是一個能夠建立或者返回一個服務的東西,最典型的就是服務類自己。

你能夠在模塊中或者在組件中註冊提供者。

一般來講,給 root module 增長提供者,這樣服務的一樣實例在任何地方都有效。

app/app.module.ts (module providers)

providers: [ BackendService, HeroService, Logger ],

做爲一種選擇,在 @Component 元數據的providers 屬性的組件層面註冊:

app/hero-list.component.ts (component providers)

@Component({ moduleId: module.id, selector: 'hero-list', templateUrl: 'hero-list.component.html', providers: [ HeroService ] })

在組件層面註冊意味着你用那個組件的每個新實例去獲取一個新的服務的實例。

關於依賴注入須要記住的點:

  • 依賴注入是裝配到Angular結構上的,能夠在任何地方使用。

  • 這個注射器是主要的裝置。

    • 一個注射器維護者一個它建立的服務實例的容器。
    • 一個注射器能夠經過provider建立新的服務實例。
  • 一個提供者是建立服務的祕訣。

  • 使用注射器去註冊提供者

總結

你已經學到了Angular應用的八個主要構件塊的基礎了。

那是在Angular應用中的任何東西的基礎,從這裏起步應該是綽綽有餘了。(注:原文是and it's more than enough to get going. )可是它不包括全部你須要知道的。

這裏有一個簡要的,字母排序的,重要的Angular功能和服務的列表。它們中的大多數會在本套文檔中覆蓋(或者即將是)。

Animations(動畫):不須要對動畫技術或CSS的深度掌握的,使用Angualr動畫庫開發動畫組件行爲。

Change detection(變動檢測):變動檢測文檔會覆蓋Angular是如何肯定一個組件的屬性值發生了改變,何時去更新屏幕,而且它是如何使用zones 去插入異步活動和運行它的變動檢測策略。

Events(事件):事件文檔將會覆蓋如何使用組件和服務去觸發事件去發佈事件或者訂閱事件。(注:原文是The events documentation will cover how to use components and services to raise events with mechanisms for publishing and subscribing to events.)

Forms(表單):使用基於HTML驗證和髒檢查來支持複雜數據場景。

HTTP:用HTTP客戶端來和一個服務器進行通訊去獲取數據,保存數據,調用服務端行爲。

Lifecycle hooks(生命週期鉤子):經過實現生命週期鉤子接口,在一個組件的生命週期的關鍵時刻嵌入,從構建到銷燬。

Pipes(管道):在模板中使用管道去提高用戶體驗,把值轉換成一種顯示。考慮這個currency 管道表達式:

price | currency:'USD':true

它把42.33的價格顯示成 $42.33

Router(路由器):在客戶端程序中的頁面間導航,而且毫不會離開瀏覽器。

Testing(測試):使用Angular Testing Platform在你的應用部分運行單元測試,就像它們在與Angular結構交互。

相關文章
相關標籤/搜索