http://www.tuicool.com/articles/EvEZjmZjavascript
本文將從框架角度簡單介紹Angular2的基本架構,若是你對Angular2還不太瞭解,請先閱讀另兩篇文章Angular2入門教程1 和 Angular2入門教程-2 實現TodoList App ,而後再閱讀此文。css
Angular2是一個完整的單頁應用開發框架。不少人拿它跟React比,相比來講,React是一個基礎框架,更像是一個庫,你須要不少第三方的庫才能方便的開發一個完整的應用。而Angular2則提供了不少組件,或者叫庫,好比Directive(指令)、組件框架、模板、依賴注入、綁定、路由等,在這些庫的幫助下,你更多的只須要關注具體業務的開發。html
當你編寫一個Angular2的應用的時,你從一個Component開始,編寫這個Component須要展現的頁面模板,並在Component中控制模板上顯示的內容和用戶的交互。通常還須要編寫一個service來實現業務邏輯。你還須要針對這個模板編寫一個樣式。最後,你須要把這個Component和service定義到一個module(模塊)裏。java
這樣就完成了這個應用模塊的開發。剩下的就是用Angular提供的方法去啓動應用的 root module
,也就是根模塊。若是你的應用有幾個模塊須要交互或跳轉,就須要定義路由。react
因此,你只須要開發業務相關的Component組件,以及組件之間的數據交互和頁面跳轉,剩下的像頁面文件的渲染、變量的雙向綁定、用戶事件的響應、頁面的跳轉和參數傳遞等等,都由框架來完成。jquery
在這篇文章裏,咱們就來看看Angular2框架背後提供的一些功能,瞭解了這些,有助於咱們理解一個Angular應用是怎麼工做的,從而幫助咱們更好的利用框架,開發出更高質量的應用。web
Angular2框架,提供瞭如下幾個基本的功能:bootstrap
模塊是一個業務功能的集合,咱們能夠把幾個組件、服務和其餘一些業務模型的定義都加到一個模塊裏,他的功能更多的是幫助咱們更好的組織咱們的代碼,方便代碼重用。模板、數據綁定、依賴注入是定義一套這個框架的規則和語法,咱們用這套規則和語法編寫的代碼,就可以享受Angular2給咱們帶來的便利。服務器
Angular2的模塊能夠將組件、服務、指令、方法等封裝成一個模塊,以下圖所示(圖片摘自官網):angular2
好比咱們開發一個系統,裏面包含一個」個人消息」的功能,這個功能包含一些組件,如消息列表、詳細詳情、回覆、新消息和好友列表等。除了這些組件,咱們又須要相應的服務來跟服務器交互來提供數據。咱們可能也須要一些環境變量等。咱們能夠把這些組件、服務等都封裝在一個方法裏面,像這樣:
import{ NgModule }from'@angular/core'; import{ CommonModule }from'@angular/common'; import{ FormsModule }from'@angular/forms'; import{ FriendModule }from'./friend/friend.module'; import{ MessageListComponent }from'./message/list.component'; import{ MessageDetailComponent }from'./message/detail.component'; import{ MessageFormComponent }from'./message/form.component'; import{ MessageService }from'./todo.service'; @NgModule({ imports: [CommonModule, FormsModule, FriendModule ], exports: [MessageListComponent], declarations: [MessageListComponent, MessageDetailComponent, MessageFormComponent], providers: [MessageService] }) exportclassMessageModule{}
在上面的模塊定義中,咱們又引入了一個 FriendModule
,由於好友的功能在另外一個模塊裏,咱們不須要從新實現,而只是引入他既能夠。咱們也能夠只引入好友模塊裏面的某一個組件,也能夠只引入服務。
在MessageModule中,在 exports: [MessageListComponent]
中導出了MessageListComponent,這樣,別的模塊可使用這個組件,來顯示消息列表。
在應用的根模塊中,除了上面的這些’imports’,’providers’這些定義之外,還有一個 bootstrap: [AppComponent]
,表示這個應用一開始會加載這個組件到index頁面中。
在Angular2中,提供了不少模塊,例如上面的 FormsModule
、 CommonModule
, 還有Router等。他們都是一個個模塊,也算是庫,能夠單獨引入使用,也能夠只引入須要的部分。
Angular2的組件是一個可重用的單元,包含模版、樣式,還有數據、事件等交互邏輯。
下面以前文章中TodoList應用中的一個組件:
import{ Component }from'@angular/core'; import{ Todo }from'../todo'; import{ TodoService }from'../todo.service'; @Component({ selector: 'todo-list', templateUrl: 'app/todo/list/list.component.html', styleUrls: ['app/todo//list/list.component.css'] }) exportclassTodoListComponent{ newTodo: Todo = newTodo(); constructor(private todoService: TodoService) { } addTodo() { this.todoService.addTodo(this.newTodo); this.newTodo =newTodo(); } get todos() { returnthis.todoService.getAllTodos(); } }
Angular2的組件經過一個@Component的Decorator(裝飾器)定義一個組件TodoListComponent,組件中有2個方法,一個用於初始化任務列表數據,一個用於相應頁面上的新建任務的事件。
在@Component中,定義了這個組件使用的模板、樣式,和在它的父組件中所在的位置,也就是html標籤 <todo-list>
。
在Angular2裏,Directive(指令)跟組件相似,工做原理也跟上面相似。它跟組件同樣,也是定義一種可重用的結構,添加用戶交互。實際上,在Angular2中,Component繼承自Directive接口,並提供了模板相關的屬性和功能。
雖然組件和指令區別不大,可是,由於它在Angular2裏面很是重要,並且是咱們開發應用的基本單位,因此它被獨立出來,並被放在Angular2框架的核心位置。
有關組件,須要說明的是,在一個Angular2的應用中,組件是一個屬性的結構,就好像html的DOM樹同樣,每一個Angular2應用都有一個根組件,而後它會有一個個的子組件。在咱們以前的一篇文章《Angular2入門教程-2》中咱們在設計系統的時候,就是從設計組件開始,獲得的是一個組件樹:
每一個組件(除了根組件)都會有一個父組件,每一個組件定義中的 selector
的值,對應父組件中的一個html標籤。
元數據就是在定義模塊、組件、服務的時候,Decorator方法裏面的參數內容,例如一個AppComponent的元數據,就是 @Component
裏面的參數,以下:
{
selector: 'root-app', templateUrl: 'app/app.component.html', styleUrls: ['app/app.component.css'] }
在Angular2中,Decorator被大量使用,當咱們定義模板、組件、服務、指令時,都是使用Decorator來定義。顧名思義,Decorator(裝飾器)就是在一個類上面添加一些額外的屬性或方法。
舉個例子,上面的根組件AppComponent,在定義它的時候,經過 @Component
才能把它定義成一個Angular的組件。而後咱們在這個元數據裏面,設置了這個組件對應的selector,模板和樣式。這樣Angular框架在解析這個類的時候,就會按照組件的規則去解析並初始化。當在一個頁面裏面遇到這個selector設置的標籤(如這個例子中的 )時,就會初始化這個組件,渲染模板生成html顯示到對應的標籤裏面,並應用樣式。
在Angular2中,服務是一個很寬泛的定義,任何的類均可以被定義成服務,這個類中能夠包含一些業務方法,能夠包含環境配置變量。Angular2也沒有對服務的定義作任何的規則限制。下面就是一個最簡單的服務:
exportclassSomeService{
someConfig: {foo: 'bar'} getConfig() { returnsomeConfig; } handle(msg: any) { console.log(msg); } }
咱們只須要定義一個class,並把它export就能夠。可是,通常咱們都是結合依賴注入來使用服務。
從Angular1的版本開始,依賴注入就是一個很核心的概念,在版本2中,主要是用於管理service實例的注入。在上面講的service中,咱們建立了一個SomeService,在傳統的用法中,咱們都是在須要用他的地方手動建立一個這個類的實例,而後調用他的相應方法或屬性,例如:
letmyService =newSomeService();
myService.handle('the message');
可是,當咱們的系統中有不少service類,甚至這些service類相互之間又須要引用的時候,咱們就沒有辦法都經過手動建立的方式來獲取service實例。更重要的是,這對於系統的解耦很是不便,不一樣的服務之間直接建立和引用,會讓系統變得很是難以維護。
Angular給咱們提供了一個很是好的解決方案,它借用了Java等語言中某些容器庫的概念,將全部service實例的建立都由容器來完成,當一個service須要引用另外一個service的時候,不是本身建立另外一個service的實例,而是從容器中獲取那個service的實例。
要使用依賴注入的功能,首先咱們的service必須由一個裝飾器 @Injectable
來定義:
@Injectable()
exportclassSomeService{
// 跟以前同樣,省略... }
而後,在Component中須要加一個providers,也就是服務的建立者:
@Component({
selector: 'some-list', templateUrl: 'some.component.html', providers: [ SomeService ] }) exportclassSomeComponent{ constructor(private theService: SomeService) { } // 省略其餘方法。 },
這樣這個服務就能夠在SomeComponent中自動注入了。它的構造函數中有一個參數theService,類型是SomeService,Angular在建立這個Component的時候,就會從容器裏面查找SomeService類的實例,若是有就用這個實例去初始化SomeComponent對象;若是沒有就先新建一個,再初始化。這個過程,就是Angular的依賴注入。
有關依賴注入,須要注意的一點就是依賴注入的做用範圍。Angular2的依賴注入是一個樹形的結構,就好像組件樹同樣。在上面的例子中,咱們在 SomeComponent
的providers
裏面設置了 SomeService
,也就是說,在SomeComponent這個節點,以及它全部的子節點的組件上,SomeService類的實例是共用的,它們都共享一個實例。可是,在這個SomeComponent的父組件裏,它若是也想注入SomeService來使用的話,就沒有辦法從容器中得到,除非在它的父組件中的providers中也添加了這個服務。
在咱們以前教程《Angular2入門教程-2》的TodoList應用實例中,咱們把todo應用相關的類都添加到一個模塊裏,內容以下:
import{ NgModule }from'@angular/core'; import{ CommonModule }from'@angular/common'; import{ FormsModule }from'@angular/forms'; import{ TodoListComponent }from'./list/list.component'; import{ TodoDetailComponent }from'./detail/detail.component'; import{ TodoItemComponent }from'./item/item.component'; import{ TodoService }from'./todo.service'; @NgModule({ imports: [CommonModule, FormsModule ], declarations: [TodoListComponent, TodoDetailComponent, TodoItemComponent], providers: [TodoService] }) exportclassTodoModule{}
咱們在這個模塊的定義中定義了 providers: [TodoService]
,這樣,這個模塊的幾個Component均可以共用這個TodoService的實例。
在jQuery或者更早的時代,當咱們須要更新頁面的內容的時候,咱們通常都須要本身得到頁面的DOM,而後,設置他的值。當頁面上的內容須要更新到js端的時候,又須要設置一些事件,如onclick, onblur等,而後在響應事件裏面再從頁面得到這個值。這不只須要些不少代碼、浪費時間,還很是容易出錯。終於,Angular把咱們從這些枯燥的工做中解放出來,提供了數據綁定的功能。
在Angular1.x的版本中,數據綁定是經過輪詢實現的。在Angular1裏,全部須要綁定的數據都會在$scope中,Angular1.x有一個輪訓機制,每隔一段時間就檢查全部綁定的變量,檢查他們如今的值跟上次檢查的值是否一致,若是不一致,就觸發相應的方法更新頁面的內容。這雖然給咱們開發帶來了便利,可是若是有太多的變量須要監聽,就會形成很大的性能問題。
在Angular2裏面,綁定的數據的監聽是經過zone.js實現。通俗來說,zone給全部有可能更新數據的方法加了一個補丁,就像AOP,或者說代理模式。每當這些更新數據的方法被調用的時候,就會觸發另外一個方法,告訴Angular有數據修改,Angular再去判斷變量是否修改,若是有修改,就更新DOM。
並且,Angular2的數據更新檢測是在每一個組件上有一個檢測器。這樣,就算應用中有再多綁定的變量,當有一個數據修改的事件之後,也只是對應的那個組件的檢測器被觸發,來檢查它以及它全部的子組件的數據修改。
這兩方面結合,就使得Angular2應用的性能可以有很大的提高。
說了那麼多原理,咱們再來看看Angular2是數據綁定的幾種方式,結合下面的代碼看看每種方式的用途。這個模板中包括一個輸入框用於新建,包含一個列表顯示,以及一個子組件:
<headerclass="header">
<inputclass="new-hero"placeholder="輸入名字"[(ngModel)]="newHero.name"(keyup.enter)="addHero()"> </header> <ul> <li>{{hero.name}}</li> <hero-detail[hero]="selectedHero"></hero-detail> <li(click)="selectHero(hero)"></li> </ul>
咱們看看這個模板裏面包含的4種數據綁定的方式:
[user] 屬性綁定
這種方式用於將當前組件中的變量傳遞到子組件,也就是從list組件中,對於每個hero,將它傳遞到子組件HeroDetailComponent中。在子組件中,就須要這樣來獲取:
@Input() todo: Todo;
(click),(keyup.enter)
這就是事件綁定,將頁面上的DOM事件綁定到組件中的某個方法上。也就是當用戶點擊(click),或者敲回車鍵後彈起(keyup.enter)時,調用組件中的某個方法。
對於上面的 []
和 ()
兩種類型的綁定,能夠理解成’輸入’和’輸出’。 []
至關於一個組件的輸入,通常這個輸入從它的父組件得到;()
至關於組件的輸出。上面的例子是用事件綁定,將數據」輸出」到組件裏。實際上,咱們還能夠用一種 EventEmitter
把數據」輸出」到父組件。
下面的圖直觀的描述了上面4種數據綁定方式的做用方式:(圖片來自官網,其中’[property] = 「value」‘這個表述的彷佛不太正確???):
爲了描述這幾個功能之間的關係,先看看下面這張圖(圖片摘自官網):
這張圖就比較清楚的說明了組件、服務、模板、Directive(指令)、數據綁定和依賴注入的相互關係。結合這張圖和上面說的開發一個Angular2的應用的基本過程,這樣就更容易理解了。
下面,咱們來看一下Angular應用的工做流程。