Angular2 初探
Angular2初探
盼星星盼月亮終于把angular2 beta盼來了,在beta前一個月快一天一個release都把小G折磨壞了,小G公司用的angular2,然后就跟著Google那些工程師一天一個update,然后還要看update把以前的啥功能break了沒,當時改rxjs的api的時候真的是想死的心都有了。還有rxjs帶來的各種 bug,終于迎來了angular2 beta,碼農翻身做主人,真是這個月最開心的事情了。接下來就是把所有code改好build pass就好。小G用angular已經兩年多了,一開始用的angular做了一個officeapp,公司看不錯就migrate到angular2 的webapp,所以小G是經歷過angular1和angular2的人,看過angular1的各種丑和angular2的各種帥。所以這篇文章主要是強推angular2的軟文,主要快速上手angular2,介紹一下angular2的主要優勢,快速接觸angular2的一些語法及 Component,Service的用法。還有就是介紹小G最喜歡的rxjs,因為angular2已經基于rxjs了,把所有東西變成stream然后各種神操作。當然一篇blog不足夠介紹這么多,所以小G會分以下幾塊:
- 本篇blog會初步介紹angular2的一些簡單語法及快速上手。
- 下一篇blog會介紹angular form 的用法
- 再下一篇blog會介紹angular2里面的dependency injection的主要用法以及用ood的方法來做dom manipulation。
- 下下篇最重要會介紹小G最愛的rxjs。
- 小G還會寫個cheatsheet,關于angular2的主要用法以及rxjs的主要用法。rxjs的主要用法按decision tree的方法展示。
參考資料
我是第一次見有公司能把framework的文檔寫的這么好的,google真是給了我驚喜。如果大家不想從頭開始寫,小G我寫了個簡單app,囊括了google angular2文檔里的tutorial。這里上鏈接:
Angular 2 quick start. 所以這個app的文件結構是這樣的:
├──angular2/ │ │ │ ├──app/ │ │ ├──boot.ts │ │ ├──app.component.ts │ │ ├──hero/ //這篇博客用 │ │ | |──hero.component.ts │ │ ├──hero-form/ //form blog 用 │ │ | ├──hero.ts │ │ | |──hero-form.component.ts │ │ | |──hero-form.component.html │ │ ├──hero-list/ //dependency injection 用 │ │ | ├──... │ ├──index.html │ ├──package.json //用來安裝我們需要的庫, 以及node的一些命令 │ ├──tsconfig.json //用來配置typescript │ ├──style.css //這個playground的css
package.json
用過前端的都知道,npm拯救了世界。所以我們用angular2的時候肯定也會用上npm來幫我們加各種庫。所以對node或者npm熟悉的肯定也知道package.json。 在package.json里面,你可以定義script:"scripts": { "tsc": "tsc", "tsc:w": "tsc -w", "lite": "lite-server", "start": "concurrent \"npm run tsc:w\" \"npm run lite\" " },
在這里你可以設置你一會要用npm跑的命令,以這個為例,如果你跑npm start,那他就會先跑npm run tsc用tsc compile typescript到javascript,然后跑npm run lite跑server來host整個app。
還有dependencies和devdepdencies,你可以在里面加你想要使用的庫,這樣你就可以用npm install安裝你所有想要的庫。這里有個小trick,如果你想把這個庫裝到系統里面,就要跑
1 |
npm install -g [test] |
如果你只想隨便使用一下,就跑:
1 |
npm install [test] |
如果你不僅想用還想加到packge.json里面,那就要使用
1 |
npm install --save [test] |
typescript.json
這個文件主要用來配置typescript的compiler,比如用啥target,用啥module,是不是要設置sourcemap,是不是要要設輸出路徑什么的。小G在這里配置一個輸出dist,這樣生成的js和map就不會和tsfile合在一起,他們會到一個單獨的文件夾,
然后index.html會去查找。
index.html
這個是整個app存在的html支持,angular2說白了還是javascript,它是不能脫離html的存在。所以跟普通的javascript一樣,需要html來load這個框架。在這個例子中小G就使用了systemjs,這個庫能找到angular2 的入口然后把整個app load出來。
當然不僅僅可以使用systemjs,還有一些其他的框架比如webpack,可以bundle你的庫讓你的庫不再成為一團亂麻。不過代價是他會有個很奇怪的build 方式,他有個server來查看你的code的改變然后在memory里面compile typescript然后把編譯好的javascript放到memory里面然后用webpack dev server host然后讓index.html直接找,這樣的壞處是在debug的時候sourcemap很難找。雖然chrome debugger可以很容易解決這個問題。但是其他編譯器比如vscode的chrome debugger extension就沒有那么智能了。小G曾經嘗試用vscode debug angular webpack starter,過程非常痛苦。
boot
Angular2 對于angular1一個最大的進步是所有的東西都模塊化了,web component好像變成了現在web technology的一個趨勢。比如google正在推崇的polymer就完全使用了web component的思想,他給你很多個component,就像搭積木一樣把你要的component下載下來,然后搭起來搭成你想要的一個web app。小G覺得這個想法是很不錯,不過小G試了下polymer反應還是有點慢,雖然那些component做的很好看很material design,但是用戶體驗太慢的話還是不行吧。但是我相信polymer一直在進步,總有一天可以很好的使用它。這個boot等于整個app的入口,在boot.ts里面加入了這個app的root component,然后在index里面用systemjs調用這個boot,就把這個app整個load起來了。具體如下:index.html | |systemjs調用boot boot |boot調用模塊化app的root component | app
而boot在調用模塊化的component root的時候使用了bootstrap()的函數,我們可以importanglar2/core來得到bootstrap()這個函數。看到這里大家可能要想為啥不直接用index load app 為啥要通過一個boot呢?確實如果只想做web app,可以直接用systemjs來load app。有boot的原因是因為我們可能不僅僅想做web application,
我們還想做native app, 還想做cordova或者ionic的app,這樣只需要替換boot其他所有的代碼都可以復用,這是angular2的一個很大的改善。angular1里面并沒有很好的支持這一個功能。
app
正如小G剛才所說,angular2把所有的組建都模塊化了,在angular2里面,你的整個app 可以變成很多個模塊,這些模塊可以定義輸入輸出,也可以繼承,可以拼接,這些模塊就是不同的component,component之間的通信,數據交換就用service。在小G寫的例子里,可以看到就加入了三個模塊:hero,hero-form,heros-list。@Component({ selector: 'my-app', template:` < hero>< /hero> < hero-form>< /hero-form> < heroes-list>< /heroes-list> `, directives:[HeroComponent, HeroFormComponent,HeroesListComponent] })
首先@Component是angular2里面新引進的函數,通過這個函數我們可以設置selector,template,directives,styles等等。當然如果你要使用Component這個函數就要引入庫:
1 |
import {Component} from 'angular2/core'; |
import {HeroComponent} from './hero/hero.component'; import {HeroFormComponent} from './hero-form/hero-form.component'; import {HeroesListComponent} from './hero-list/heroes-list.component';
selector
主要用來定義外部的模塊如何使用這個模塊。比如在app.component中selector是my-app,那在外部的index.html里面要load app.component這個模塊就要使用
template
主要用來定義這個模塊里面的html結構,他也可以加入內部的模塊,比如這個 app.component就加入了三個不同的子模塊。這樣寫html是不是好像就像搭積木一樣簡單了?有一點要注意的是當template引用html 的時候需要使用‘`’符號,一般這個符號在鍵盤上數字鍵1的左邊。在template里還有一個很重要的概念,用過angular1的童鞋都知道怎么在html里面引用controller里面的變量,沒錯,用{ { } },在angular2里面也一樣。 我們在class里面定義了一個變量,在template里面就可以用{ { } }直接引用。例如:
@Component({ selector:'hero', template: ` < div >{{title}}< /div > ` }) export class HeroComponent { public title = 'Tour of Heroes'; }
這樣可以在template直接引用class里面的title,等于template里面是< div>Tour of Heroes< /div>
然后你就可以隨意改這個變量,然后render出來的html也會跟著改變。
另外我在template里面想提到的是@Input @Output factory method以及[(ngModel)]的雙向綁定。
@Input
這個@Input是用來定義模塊的輸入的,用來讓父模塊往子模塊傳遞內容:@Component({ selector: 'bank-account', template: ` Bank Name: {{bankName}} Account Id: {{id}} ` }) class BankAccount { @Input() bankName: string; @Input('account-id') id: string; // this property is not bound, and won't be automatically updated by Angular normalizedBankName: string; } @Component({ selector: 'app', template: ` < bank-account bank-name="RBC" account-id="4747">< bank-account> `, directives: [BankAccount] }) class App {}
再這個例子中,父模塊是app,子模塊是BankAccount, 在app中我們想往子模塊里面傳遞back-name和account-id這兩個信息,可以通過property的方式最簡單直接,而子模塊要接受這個 property就要用@Input來接收。子模塊會在BankAccount里面直接接收傳遞過來的property。傳遞的方式可以是駝峰法,也可以直接寫在input里面,就如
例子里面寫的那樣。要記住一點的是父模塊在引用子模塊的時候是用的[]。
@Output
如果我們的子模塊想自定義一些event傳遞給父模塊又該怎么做呢?這時候就要用到@Output了。@Directive({ selector: 'interval-dir', }) class IntervalDir { @Output() everySecond = new EventEmitter(); @Output('everyFiveSeconds') five5Secs = new EventEmitter(); constructor() { setInterval(() => this.everySecond.emit("event"), 1000); setInterval(() => this.five5Secs.emit("event"), 5000); } } @Component({ selector: 'app', template: ` < interval-dir (every-second)="everySecond()" (every-five-seconds)="everyFiveSeconds()"> < /interval-dir> `, directives: [IntervalDir] }) class App { everySecond() { console.log('second'); } everyFiveSeconds() { console.log('five seconds'); } }
因為在普通的html里面,比如button我們會有onclick這種event來監聽這個button是否被按了,然后angular2也完全允許我們自定義這種event listening的模式,我們可以給任何模塊定義這種event,當觸發了event之后就會從子模塊往父模塊傳遞子模塊的信息。
再這個例子里,父模塊是app,子模塊是IntervalDir, 然后在子模塊定義event觸發的條件,比如每隔1s要觸發事件,每隔5s要觸發事件。然后當父模塊監聽到這些事件時可以做相應的操作。當然我們如果想傳遞信息,可以在new EventEmitter()里面加入我們想要傳遞的東西,然后在使用的時候加入函數的參數里面。比如我們想傳遞一個test,我們只需要這么改:
class IntervalDir { @Output() everySecond = new EventEmitter(test); } < interval-dir (every-second)="everySecond(test)" (every-five-seconds)="everyFiveSeconds()"> everySecond(test) { console.log(test); }
而在這里,父模塊在引用子模塊的時候是用的()。
[(ngModel)]
最后當然是最經典的angular特有的ngModel雙向binding了。而這個特性是< input >特有的。我們注意到了 [(ngModel)] 既有[]也有()。沒錯它既有Input的特性也有Output的特性。
1 |
< input [(ngModel)]="hero.name" placeholder="name" > |
在這里,我們在view里面改input里面的內容,hero.name也會跟著改變,而如果我們在class里面改變hero.name在view里面的內容也會跟著改變。然后就是mvvm的世界了。
directives
主要是加入子模塊的模塊,當我們定義了一個模塊之后,如果我們想要它被別的模塊使用,必須定義一個出口被別的模塊使用,比如app.component這個模塊最后就要:
1 |
export class AppComponent {} |
讓angular2知道它是一個AppComponent模塊,然后在boot里面可以加入這個模塊。同理我也export了HeroComponent,HeroFormComponent以及HeroesListComponent模塊。然后在app.component的一開始就import進來,然后就要在directive加入這些模塊然后在template才能識別相應的selector。如果
不在directive中引入相應的模塊,那在compile的時候template就不知道相應的selector是啥就不能正常的render了。
providers
這個主要用來加入service的模塊。當我們想在模塊之中使用service的時候,和directive相同的道理,我們需要加入provider這樣這個模塊就知道使用的service的provider。小G在用angular的過程中碰到最多的問題就是missing provider的問題,在angular1中可能是由于service的dependency之間有loop,
表示service互相依賴,會有問題。但angular2由于模塊化了這個dependency injection的問題得到了很好的解決。如果再碰到這類問題首先要檢查模塊是否加入了service provider。
styles
這個主要定義這個模塊使用的css,不用放到外部的style.css里面。這樣這個模塊的css完全由自己掌控,是不是方便了很多?
這整個app的root component介紹完了,這個root component主要功能是加載了其他模塊。這個博客還會介紹一個hero component。
hero component
這塊主要介紹的就是template里面的input和output還有ngFor:< li *ngFor="#hero of heroes" [class.selected]="hero === selectedHero" (click)="onSelect(hero)"> < span class="badge">{{hero.id}}< /span> {{hero.name}} < /li >
比如我們有一個list的heros,我們可以在html里面用ngFor來循環這個list然后把list里面的英雄輸出到html里面。在這里的語法和angular1完全不一樣了。在angular1里面,完全沒有以及#的符號,但是在angular2里面,和#確實很關鍵的語法。
( )這個在ngFor的前綴表示整個< li >元素以及它的子元素合成一個master模板。如果不想使用( ),ngFor需要被包含在一個template的元素里面:
< template ngFor #hero [ngForOf]="heroes" > < hero-detail [hero]="hero" >< /hero-detail > < /template >
Angular2還有這個語法的就是ngIf。如果不用()也需要把ngIf包含在template里面。
而在hero前面的( # )表示了hero是一個本地模板變量,整個合起來就是循環heros的list,然后對于每個hero,在< li >< /li >內可以進行操作。
從上面的例子還可以看到input和output。只不過這兩個都已經在li模塊里面集成好了,不需要自己定義。所以對于li這個元素的input,我們可以定義這個li的class的是否被選擇。對于li元素的output我們可以定義點擊事件監聽。然后在這個模塊中進行相應的函數操作。
小結
洋洋灑灑這么多,其實angular2的主要知識點有下面幾個:
- Angular2的文件結構,typescript的設置,package.json的設置。
- boot的作用,更換boot可以達到復用代碼讓代碼工作在其他framework的作用。
- Angular2是模塊化的,自上而下的一個樹狀結構。每個模塊之間可以互相調用,也可以用service互相通信。
- Angular2里面的一些概念,比如Component,Directive,Provider,selector,template, @Input, @Output, [ ( ngModel) ], ngFor, ngIf.
如果你知道上面這些是咋回事,那差不多可以開始自己寫個angular2的app啦。一方面可以在小G的簡單app上隨便操作看效果,另一方面小G也給個angular2的快速開發app的project:angular2-webpack-starter。小G就是用這個來做比較大型的app開發的。這個項目是跟著angular2不斷在更新的,比較傷感的是用vscode很難debug。
本文標題:Angular2 初探
文章作者:G
發布時間:2015年12月21日 - 23時30分
最后更新:2015年12月23日 - 15時39分
原始鏈接:http://gabriel0402.github.io/2015/12/21/overview/
許可協議: "署名-非商用-相同方式共享 3.0" 轉載請保留原文鏈接及作者。