Angular 4 簡單入門筆記
剛實習的時候用過AngularJS,那時候真的是連原生JavaScript都不會寫,依樣畫葫蘆做了幾個管理後台。然後突然換項目了,AngularJS就不寫了,感覺前前後後接觸了一年多的AngularJS,結果只懂點皮毛。
最近又有個管理後台的需求,決定再拾起,但現在是升級版的Angular了。終於,有機會好好再看一眼Angular了,這次希望能深入一點了解。
本文是筆者在學習開發過程中的總結輸出,目的在於讓初次接觸Angular的開發者對該框架能有整體的認識,並且能快速上手開發工作。
AngularJS VS Angular
AngularJS最大版本號只有1.x,2.x/4.x的版本號都是針對於全新的框架Angular。但不能說Angular和AngularJS一點關係都沒有,你看名字這麼像,是吧?!回憶一下AngularJS被人念念不忘的特性,雙向數據綁定,MVC,指令,服務,過濾器,模塊化,臟檢查機制,依賴注入,Scope,路由,表單校驗等等。
看下AngularJS到Angular的過程中,哪些概念被保留下來,哪些被剔除了(所謂的取其精華,去其糟粕)。
剔除的部分:
- ng-controller指令:控制器主要是業務邏輯的控制部分
- $scope概念:很強大又很複雜
- 數據雙向綁定:數據雙向流通可能導致數據的震蕩(故才有最多檢查10次的限制,10次之後還不穩定就報錯)
保留/改善的部分:
- 路由嵌套:AngularJS自帶的路由系統是不能嵌套路由的,到了Angular你想怎麼嵌套就怎麼嵌套
- 過濾器(Filter)變成管道(Pipe),概念的變化
- 依賴注入機制:直接在構造器中注入,還有分層依賴注入的概念
- 指令寫法:
- (事件) ng-click變成(click)
- [屬性] href = "{{}}"可以寫成 [href]
- [(ngModel)]代替以前的ng-model
- *ngFor 代替 ng-repeat,不適用於對象,適用任何有Symbol.iterator屬性的數據結構(能用for...of來訪問),比如數組,集合等
- *ngIf 代替 ng-if,去掉ng-show,ng-hide
- 對移動端的支持
- 模版,數據綁定,服務,模塊,臟檢查機制等
新增的部分:
- 組件化:Angular的核心所在
- Typescript作為默認的開發語言
- ZoneJS監聽所有(可能導致數據變化)的非同步事件
- 支持服務端渲染
Angular Cli
Angular團隊為開發者提供了一個開箱即用(out of the box)的腳手架工具:Angular Cli。我們再也不用擔心在項目初始化時,要搭建配置一系列的工具,比如webpack,karma,tslint,protractor等。
操作很簡單,只要運行如下命令行就搞定了。
具體的語法教程可參考這裡。
安裝之後,文件目錄如下:
my-dream-app e2e // 端到端測試 app.e2e-spec.ts app.po.ts tsconfig.e2e.json node_modules/... // npm包 src/... // 源碼 angular-cli.json // 配置項 .editorconfig // 編輯器配置 .gitignore // git忽略文件配置 karma.conf.js // karma配置 package.json // npm配置 protractor.conf.js // 測試配置項 README.md // 項目說明 tsconfig.json // ts編輯器的配置 tslint.json // tslint配置項
我們需要關注的是src文件夾,這裡存放我們所有的源代碼,開發的時候基本都在src中。
src app // 代碼的主要文件夾 app.component.css // 根組件樣式 app.component.html // 根組件模版 app.component.spec.ts// 根組件測試 app.component.ts // 根組件腳本 app.module.ts // 根模塊 assets // 靜態資源 .gitkeep // 保存空文件夾 environments // 環境配置 environment.prod.ts environment.ts favicon.ico // 圖標 index.html // 頁面主入口 main.ts // 腳本主入口 polyfills.ts // 兼容瀏覽器 styles.css // 全局css樣式 test.ts // 單元測試主入口
模塊
Angular很重要的概念之一仍然是模塊。Angular整個框架就是由很多個模塊組成的,而不同的模塊需要從不同的地方導入。打開package.json文件,可以看到依賴的angular包可能是這樣的:
"@angular/common": "^2.3.1","@angular/compiler": "^2.3.1","@angular/core": "^2.3.1","@angular/forms": "^2.3.1","@angular/http": "^2.3.1","@angular/platform-browser": "^2.3.1","@angular/platform-browser-dynamic": "^2.3.1","@angular/router": "^3.3.1",
來簡單看下這些angular包中包含了哪些常用的模塊(至少目前為止,我覺得常用的)。
- @angular/core:這裡包含了很多常用的模塊
- NgModule:模塊定義裝飾器
- Component:組件定義裝飾器
- Directive:指令定義裝飾器
- Pipe :管道定義裝飾器
- PipeTransform:管道介面
- Injectable:服務定義裝飾器
- ElmentRef:元素引用
- ViewChild:獲取子元素
- Render:渲染
- Input:接受參數輸入
- Output:事件輸出
- EventEmitter:觸發自定義事件
- @angular/common
- CommonModule:通用模塊,包含內置指令ngIf,ngFor
- @angular/forms
- FormsModule:定義模版驅動表單
- ReactiveFormsModule:定義響應式表單
- FormGroup, FormArray, FormControl, FormBuilder:響應式表單元素
- Validators:表單校驗
- @angular/http
- HttpModule:http請求模塊
- @angular/router
- RouterModule 路由模塊
- Routes 路由數據結構
- @angular/platform-browser
- platformBrowser:AoT編譯
- BrowserModule:瀏覽器支持,注意該模塊導入了CommonModule,然後導出去,所以引用了這個模塊也就引用了CommonModule
- @angular/platform-browser-dynamic
- platformBrowserDynamic:JIT編譯
以上模塊都是Angular框架中的自帶模塊,而我們開發的完整單元也是模塊。一個應用中至少要有一個模塊,也就是根模塊。 一些共享的功能屬性我們可以抽象出來,成為共享模塊。然後就是一些特性模塊了。
模塊的組成由組件,服務,指令,管道等等組成,這些概念會在下面講到。定義模塊的語法如下:
@NgModuel({ declarations: [], // 用到的組件,指令,管道 providers: [], // 依賴注入服務 imports: [], // 導入需要的模塊 exports: [], // 導出的模塊,跨模塊交流 entryComponents: [] // 需提前編譯好的模塊 bootstrap: [] // 設置根組件 })export class AppModule { }
所有用到的組件,指令,管道,模塊都需要事先在模塊中聲明好,才能在具體組件中使用。服務可以在模塊,組件,指令中的providers聲明,也可以直接在運行時提供(參見Trotyl Yu的例子)。
一般情況下,在根模塊的bootstrap中設置啟動的根組件即可,但也可以動態處理(參見Trotyl Yu的例子)。
那如何啟動根模塊呢?
在入口腳本中,也就是Angular Cli項目中的main.ts中,啟動如下:
// 導入需要模塊import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";// 根模塊import { AppModule } from "./app/app.module";// 編譯啟動模塊platformBrowserDynamic().bootstrapModule(AppModule);
至此,我們對模塊有所了解,也知道了模塊的定義。
組件
自從採用組件化的React大火之後,目前市面上炙手可熱的框架全都採用了組件化的理念,Angular當然也不能落後了。可以說,組件化是Angular的核心理念。按Angular在中國的佈道者 @大漠窮秋的話來說,就是:
Angular的核心概念是組件,模塊化機制NgModule是為組件化服務的,實際上所有其它機制都是圍繞組件化而來的。只有從組件化這個角度才能把握Angular的精神內核。
組件通常都是由模版和業務邏輯組成,看一下如何用Angular寫一個很簡單的組件:
// hello.component.tsimport { Component } from "@angular/core";@Component({ selector: "hello", template: "<p> {{greeting}} </p>", styles: [`p { color: red;}`]})export class HelloComponent{ private greeting: string; constructor(){ this.greeting = "Hello, Angular2!"; }}// 使用<hello></hello>// 渲染結果<hello> <p> Hello, Angular2! </p> </hello>
定義類HelloComponent的時候,加上裝飾器@Component(Typescript語法),告訴Angular這個類是組件類。裡面的數據稱之為元數據(metadata),selector屬性說明了該組件對外的使用標記,template就是組件的模版,styles是組件的樣式。而HelloComponent中定義的就是該組件的業務邏輯了。
如果模版內容太多,可以單獨寫在一個html文件中,用templateUrl屬性引入;同理,樣式文件用styleUrls引入。
組件生命周期
正如其他框架的組件,Angular的組件也是有生命周期這個概念。在不同的階段不同的場景下,可以調用不同的生命周期函數鉤子(hook)。
- constructor:構造器函數,一般用於注入服務
- ngOnChanges:檢測到輸入數據變化,首次觸發發生在ngOnInit前。注意對象的屬性發生變化時監聽不到
- ngOnInit:組件初始化,通常會設置一些初始值
- ngDoCheck:手動觸發更新檢查
- ngAfterContentInit:內容初始化到組件之後
- ngAfterContentChecked:內容變更檢測之後
- ngAfterViewInit:視圖 初始化之後
- ngAfterViewChecked:視圖發生變化檢測之後,這個可以用來保證用戶視圖的及時更新
- ngOnDestroy:組件註銷時的清理工作,通常用於移除事件監聽,退訂可觀察對象等
具體說明可以參考這裡。
組件通信
可以想像得到,組件化的頁面結構最終會形成一顆組件樹。盜一張Vue的圖:
不可避免,我們需要考慮父子組件之間的參數傳遞問題。Anuglar提供的通信方式有如下幾種:
- 父組件到子組件:父組件用屬性綁定將值傳入,子組件通過@Input來接收。
// 父組件import { Component } from "@angular/core"; @Component({ selector: "hero-parent", template: `<h2> heroes </h2> <hero-child *ngFor="let hero of heroes" [hero]="hero" > </hero-child> `})export class HeroParentComponent { heroes = [{ name: "John" }, { name: "Lily" }]; }// 子組件import { Component, Input } from "@angular/core";import { Hero } from "./hero"; @Component({ selector: "hero-child", template: ` <h3>{{hero.name}}</h3> `})export class HeroChildComponent { @Input() hero: Hero; }
- 子組件到父組件:子組件自定義事件用@Output傳出,父組件用事件綁定獲取。
// 子組件import { Component, EventEmitter, Output } from "@angular/core";@Component({ selector: "my-voter", template: ` <h4>{{name}}</h4> <button (click)="vote(true)">Agree</button> `})export class VoterComponent { @Output() onVoted = new EventEmitter<boolean>(); vote(agreed: boolean) { this.onVoted.emit(agreed); }}// 父組件import { Component } from "@angular/core";@Component({ selector: "vote-taker", template: ` <h2>Should mankind colonize the Universe?</h2> <h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3> <my-voter *ngFor="let voter of voters" [name]="voter" (onVoted)="onVoted($event)"> </my-voter> `})export class VoteTakerComponent { agreed = 0; disagreed = 0; voters = ["Mr. IQ", "Ms. Universe", "Bombasto"]; onVoted(agreed: boolean) { agreed ? this.agreed++ : this.disagreed++; }}
- 子組件引用:在父組件模版中添加對子組件的引用,即可通過該子組件去訪問子組件的方法。
<h3>Countdown to Liftoff (via local variable)</h3><button (click)="timer.start()">Start</button><button (click)="timer.stop()">Stop</button><div class="seconds">{{timer.seconds}}</div><countdown-timer #timer></countdown-timer>
- @ViewChild():類似的,也可以在腳本中用@ViewChild()來獲取子組件
import { AfterViewInit, ViewChild } from "@angular/core";import { Component } from "@angular/core";import { CountdownTimerComponent } from "./countdown-timer.component"; @Component({ selector: "countdown-parent-vc", template: ` <h3>Countdown to Liftoff (via ViewChild)</h3> <button (click)="start()">Start</button> <button (click)="stop()">Stop</button> <div class="seconds">{{ seconds() }}</div> <countdown-timer></countdown-timer> `})export class CountdownViewChildParentComponent implements AfterViewInit { @ViewChild(CountdownTimerComponent) private timerComponent: CountdownTimerComponent; seconds() { return 0; } ngAfterViewInit() { setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0); } start() { this.timerComponent.start(); } stop() { this.timerComponent.stop(); }}
- 將數據保存在服務中
- @ngrx/store:參見【譯】手把手教你用ngrx管理Angular狀態
模板與數據綁定
模版說白了就是html的內容,常規的html基本都是靜態內容,而模版結合了框架中的新語法使得html動態化。來看看Angular中的模版有什麼便利的語法:
- 插值綁定:雙花括弧{{}}
我們可以看到上一節組件例子中的{{greeting}}就是插值綁定。不僅可以獲取變數的值,還可以直接寫表達式。
- 屬性(Property)綁定
<input [value]="myData">
還有其他的,比如樣式綁定:
<div [ngClass]="{special: isSpecial}"></div>
注意點:property和attribute不一樣,想要綁定attribute,你需要寫成property。比如:
<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
你將會得到如下錯誤信息:
Template parse errors:Can"t bind to "colspan" since it isn"t a known native property
你需要改寫成這樣:
<tr><td [attr.colspan]="1 + 1">One-Two</td></tr>// 或者<tr><td attr.colspan="{{1 + 1}}">One-Two</td></tr>
- 事件綁定
<input (keyup)="handle($event)" >
可以是原生的事件:click,change,keydown,mousemove等,也可以是自定義事件,也可以是指令事件,比如ngSubmit。
- 雙向綁定
<input [(ngModel)] = "data">// 雙向綁定的背後其實是單向綁定和事件觸發,等價於下面<input [ngModel]="data" (ngModelChange)="data=$event">
注意點:使用ngModel,需要引入FormsModule模塊。
還有些內置的指令:
- 模版引用變數(# / ref-)
可以在元素上用#或者ref-前綴來標記這個元素,然後在其他地方引用。
<input #fax placeholder="fax number">( <input ref-fax placeholder="fax number"> )<button (click)="callFax(fax.value)">Fax</button>
- *ngIf:控制內容的有無
<div *ngIf="show"> Can you see this? </div>
如果還有else部分,可以如下操作:
<div *ngIf="show; else elseBlock"> Can you see this? </div><ng-template #elseBlock> else block </ng-template>
- *ngFor:循環
<div *ngFor="let hero of heroes; let i=index> {{i}}: {{hero.name}}</div>
具體的模版語法可以參考這裡。
路由
一個模塊有了多個組件之後,需要用路由來配置哪個url呈現哪個組件。
首先,我們需要在入口頁面的index.html中配置根路徑:
...<head><base href="/">...</head>...
然後創建一個路由模塊:
import { NgModule } from "@angular/core";import { RouterModule, Routes } from "@angular/router"; ...// 路由配置const appRoutes: Routes = [ { path: "home", component: HomeComponent }, { path: "heroes", component: HeroesComponent }, { path: "", redirectTo: "/home", pathMatch: "full" }, { path: "**", component: PageNotFoundComponent }]; @NgModule({ imports: [ RouterModule.forRoot(appRoutes) ], exports: [ RouterModule ]})export class AppRoutingModule {}
在主模塊中導入配置好的路由模塊:
import { NgModule } from "@angular/core";import { BrowserModule } from "@angular/platform-browser";import { FormsModule } from "@angular/forms"; ... @NgModule({ imports: [ BrowserModule, FormsModule, AppRoutingModule ], declarations: [ AppComponent, HomeComponent, HeroesComponent, PageNotFoundComponent ], bootstrap: [ AppComponent ]})export class AppModule { }
而在頁面中需要一個容器<router-outlet></router-outlet>去承載:
import { Component } from "@angular/core"; @Component({ selector: "my-app", template: ` <h1>Angular Router</h1> <nav> <a routerLink="/home" routerLinkActive="active">Home</a> <a routerLink="/heroes" routerLinkActive="active">Heroes</a> </nav> <router-outlet></router-outlet> `})export class AppComponent { }
上面代碼中的routerLink定義了用戶點擊後的路由跳轉,routerLinkActive定義該路由激活時的樣式類。
路由上還可以帶上一些索引參數:
{ path: "heroes/:id", component: HeroesComponent },
獲取的方式:
import { ActivatedRoute, Params } from "@angular/router";...export class a { constructor( private route: ActivatedRoute ) {} // 路由參數 this.route.params}
當模塊很多,路由也很多的時候,我們可以使用模塊懶載入的方式。懶載入的方式也很簡單,在配置路由的時候修改如下即可:
const routes: Routes = [ { // 默認轉到訂單管理 path: "", redirectTo: "/order", pathMatch: "full" }, { path: "order", loadChildren: "./order/order.module#OrderModule" }, { path: "warehouse", loadChildren: "./warehouse/warehouse.module#WarehouseModule" }, { path: "statistics/sales", component: SalesComponent }];// 在子模塊中用RouterModule.forChildimport { NgModule } from "@angular/core";import { RouterModule } from "@angular/router"; import { OrderComponent } from "./order.component";const orderRoutes = [ { path:"", component: OrderComponent }];@NgModule({ imports: [RouterModule.forChild(orderRoutes)], exports: [RouterModule]})export class OrderRoutingModule {}
服務與依賴注入
服務是什麼概念?可以簡單地認為它是一個功能模塊,重要在於它是單例對象,並且可以注入到其他的地方使用。
依賴注入是來自後端的概念,其實就是自動創建一個實例,省去每次需要手動創建的麻煩。
在Angular中定義一個服務很簡單,主要在類之前加上@Injectable裝飾器的功能。這是最常見的依賴注入方式useClass,其他具體參見這裡。
import { Injectable } from "@angular/core"; @Injectable() export class Service { counter: number = 0; getData(){ return this.counter++; }}
然後在模塊的providers中聲明:
import { Service } from "./service";...@NgModule({ imports: [ ... ], declarations: [ ... ], providers: [ Service ], // 注入服務 bootstrap: [...]})export class AppModule {}
使用的時候需要在構造器中建立關聯:
import { Component } from "@angular/core"; import { Service } from "./service";...@Component({ selector: "my-app", templateUrl: "./app.component.html", styleUrls: ["./app.component.css"]})export class AppComponent { constructor(public service: Service) { // this.service被成功注入 // 相當於 this.service = new Service(); // 然後可以調用服務 this.service.getData(); }}
由於該服務是在模塊中注入,所以該模塊中的所有組件使用這個服務時,使用的都是同一個實例。
除了在模塊中聲明,還可以在組件中聲明。假設AppComponent下還有組件HomeComponent,此時我們在AppComponent中注入這個服務:
import { Component } from "@angular/core"; import { Service } from "./service";...@Component({ selector: "my-app", templateUrl: "./app.component.html", styleUrls: ["./app.component.css"], providers: [ Service ], // 注入服務})export class AppComponent { constructor(public service: Service) { // this.service被成功注入 // 相當於 this.service = new Service(); // 然後可以調用服務 this.service.getData(); }}
如果HomeComponent也使用了這個服務,那它使用的將是同一個實例。這個可以從Service中的數據變化來看出。
Angular還有個分層依賴注入的概念,也就是說,你可以為任一組件創建自己獨立的服務。就像上面的例子,如果想要HomeComponent不和它的父組件同使用一個服務實例的話,只要在該組件中重新注入即可:
...@Component({ selector: "home", templateUrl: "./home.component.html", styleUrls: ["./home.component.css"], providers: [ Service ], // 重新注入服務})export class HomeComponent { ...}
對於前後端的介面,通常會寫成服務。下面說下請求後端數據這塊應該怎麼寫。在模塊這節中提過,http有專門的HttpModule模塊處理請求。首先要在模塊中導入HttpModule,然後引入http服務,調用相應的請求方法即可。
import { Injectable } from "@angular/core";import { Http } from "@angular/http"; import "rxjs/add/operator/toPromise"; @Injectable()export class HttpService { constructor(private http: Http) {} getFromServer():any { return this.http.get(`/data`) .toPromise() .then(res => res.json()) .catch(); }}
由於請求返回的對象是個可觀察對象,可以轉成Promise對象處理。這裡需要用到RxJS的toPromise操作符,然後用then去處理返回成功結果,catch處理失敗情況。這樣就搞定了後端數據的請求了。
RxJS又是另外一個比較高深的話題了,有機會深入學習一下再聊。
指令
Angular的指令概念跟AngularJS的指令差不多,最重要的區別在於Angular中的組件繼承指令,算是特殊的指令。我們看下用指令的方式去寫組件的簡單例子:
import { Directive,Input,ElementRef } from "@angular/core";@Directive({ selector: "hello"})export class HelloDirective { @Input() name: string; constructor(private el: ElementRef) {} public ngOnInit(): void { this.el.nativeElement.innerText = `hello ${this.name}!`; }}// 使用組件指令<hello name="Yecao"></hello>// 渲染結果<hello> hello, Yecao! </hello>
不要忘記在使用前先在模塊中聲明哦,我覺得這是Angular最煩人的一點。
除此之外,還有屬性指令和結構指令,屬性指令只改變元素的樣式或者行為。要寫成屬性指令,需要在selector屬性中用[]包裹起來。來看簡單地例子:
import { Directive, ElementRef, Renderer2 } from "@angular/core"; @Directive({ selector: "[highLight]" }) export class HighLightDirective { constructor(private el: ElementRef, private renderer2: Renderer2) { } ngAfterViewInit() { this.renderer2.addClass(this.el.nativeElement, "highlight"); } }// 使用屬性指令<p highLight> 這一段會高亮顯示 </p>
結構指令就是模板中提到的*ngIf,ngFor等指令,它修改了DOM結構。舉個例子,重寫ngIf:
import { Directive, Input, ViewContainerRef, TemplateRef } from "@angular/core"; @Directive({ selector: "[myIf]" }) export class MyIfDirective { constructor(private templateRef: TemplateRef<any>, private viewContainer: ViewContainerRef) { } @Input() set appMyIf(condition: boolean) { if (condition) { this.viewContainer.createEmbeddedView(this.templateRef); } else { this.viewContainer.clear(); } } } // 使用結構指令<p *myIf="false"> 這一段不會顯示 </p>
管道(過濾器)
管道其實就是過濾器,就是叫法不一致而已。主要用于格式化源數據,而不改變源數據。定義和使用的方式也很簡單:
import { Pipe, PipeTransform } from "@angular/core"; /* * 訂單取消狀態:默認為ALL表示全部,CANCEL表示已取消,NOTCANCEL表示正常*/@Pipe({ name: "cancelStatus" })export class CancelStatusPipe implements PipeTransform { transform(status:string, blank: boolean):string { const map = { "ALL": "全部", "NOTCANCEL": "正常", "CANCEL": "已取消", "": "暫無", } return blank? "特殊情況": map[status]; }}
使用前記得在模塊的declarations聲明,或者導到共享模塊,在共享模塊中導出去。使用如下:
{{ "ALL" | cancelStatus }} // 全部{{ "ALL" | cancelStatus: true }} // 特殊情況
Angular內置了一些管道:
// 日期 DatePipe{{ expression | date:"MM/dd/yy" }} // 數字 DecimalPipe,digitInfo的組成 {minIntegerDigits}.{minFractionDigits}-{maxfractionDigits}// minIntegerDigits:整數部分保留最小的位數,默認值為1.// minFractionDigits:小數部分保留最小的位數,默認值為0.// maxFractionDigits:小數部分保留最大的位數,默認值為3.{{ expression | number[:digitInfo] }}// 大寫{{ expression | uppercase }}// 小寫{{ expression | lowercase }}
後語
由於篇幅的限制,Angular的每個特性都點到為止,只是講了一些基本概念和使用方法(我也只會這點而已),讓你在項目中會用。還有一塊項目中肯定會用到的是表單及其校驗,這是個大頭,還是放在下一篇單獨拎出來說吧。
如果你看到了這裡,謝謝你花了那麼多時間閱讀。最近剛淘了視頻,出自這裡。 跟大家分享一下,鏈接: http://pan.baidu.com/s/1c2CGkVY 密碼: xwg6。
整體來說,接觸Angular2不到一個月的時候,現在項目開發中。簡單說下我的學習路徑:
- 大致瀏覽了下有關Angular2的文章,跟Angular1的比較,有個大體的印象
- 看參考資料中的幾個視頻教程,我覺得蠻不錯的,讓我對Angular2有個整體的概念
- 參考官網教程做了一下英雄展示板的例子
- 開始上手開發,邊開發邊去看文檔
- 開發的時候可以嘗試一些新的知識點,比如多模塊,共享模塊,路由懶載入,自定義表單驗證指令,響應式表單,ngrx狀態管理等等
- 總結輸出,也就是現在在寫的這邊博客
參考資料
- Angular官網(英文)
- Angular Cli
- Angular官網(中文)
- 官網英雄展示板例子
- 英文視頻教程
- Angular2一小時快速入門
- 大漠窮秋 Angular2 0視頻教程
- angularjs 1 和 2區別,這才是Angular2的靈魂!
- Redux你的Angular 2應用--ngRx使用體驗
- Angular 4 指令快速入門
本文首發於野草園,轉載請註明出處。不當之處,歡迎批評指正!
最後感謝 @Trotyl Yu @大漠窮秋 指出了文中的錯誤!
推薦閱讀:
※新手小白想系統性學習angular2,不知從何學起,求大神指點?
※為什麼有一些Angular開發人員再也不想用它了?
※如何了解Angular(非Angular.js)的實現原理?
※angular2中數據狀態管理方案有哪些?
※PC 前端是不是沒希望了?