隨著rc(release candidate,候選版本)版本的推出,萬眾矚目的angular2終於離正式發佈不遠啦!五月初舉辦的ng-conf大會已經過去了整整一個月,大多數api都如願保持在了相對穩定的狀態——當然也有router這樣的例外,在rc階段還在大面積返工,讓人頗為不解——不過總得說來, ...
隨著rc(release candidate,候選版本)版本的推出,萬眾矚目的angular2終於離正式發佈不遠啦!五月初舉辦的ng-conf大會已經過去了整整一個月,大多數api都如願保持在了相對穩定的狀態——當然也有router這樣的例外,在rc階段還在大面積返工,讓人頗為不解——不過總得說來,現在學習angular2不失為一個恰當的時機。
Google為angular2準備了完善的文檔和教程,按理說,官網(https://angular.io)自然是學習新框架的最好教材。略顯遺憾的是,在Basic章節的Overview部分中,作者在開篇就明確提到這是一份準備給有經驗的開發者的一份文檔(This is a practical guide to Angular for experienced programmers who are building client applications in HTML and TypeScript)。換言之,如果要較好地理解官網上的文檔和教程,對閱讀者是有一定要求的。最顯而易見的是,在angular2中,連運行入門級的hello world都需要配置編譯環境和後端支持——別忘了,即使是去年火遍大江南北的react,我們在學習hello world時還能通過script引入babel來在運行時解析jsx呢(當然在生產環境上可不能這麼乾)。如果要細數下來,要求就更多了:node和npm、webpack或者systemjs之類的打包和模塊化工具、面向對象編程、es6的基本語法和概念、Web Components等新標準……
博主在新技術新框架面前並不是一個因循守舊的人,只是有一點讓我比較擔憂:如果angular2的入門門檻太高,那對以後的框架推廣和社區發展會不會有一些不利的影響呢?出於這個想法,博主決定推出一個step by step的博客系列,希望將angular2帶給更多感興趣、但可能暫時缺少相關開發經驗的同學。
下麵就是本系列博客的第一篇:Hello World與自動化環境搭建。
官網在quickstart中使用了systemjs、lite-server,配置較為複雜;為了簡單起見,本文將使用大家更熟悉的gulp、browserify方案。出於相同的目的,編譯的目標版本將選擇es6而非es5,所以請讀者在繼續閱讀之前請確保已經將chrome升級到最新版本。另外,node環境也請先安裝好,具體步驟這裡就不贅述了。
1. 創建項目,初始化package.json
創建一個新文件夾angular2-learn,然後使用輸入npm init命令初始化npm環境。
npm init
如果沒有特殊需求,這裡可以一直使用回車鍵,直到整個過程完成。此時查看angular2-learn文件夾,應該多了一個package.json文件,它最重要的作用之一是保存當前項目所需要的依賴。
2. 使用npm安裝運行依賴
在正式開發之前,我們需要將angular2的相關類庫下載到本地,以便於之後的打包和運行。在本篇教程中,我們暫時只需要如下幾個依賴:@angular/common, @angular/compiler, @angular/core, @angular/platform-browser, @angular/platform-browser-dynamic
在angular2-learn文件夾下,執行如下命令:
npm install --save @angular/common @angular/compiler @angular/core @angular/platform-browser @angular/platform-browser-dynamic --registry=https://registry.npm.taobao.org
執行完成後,再檢查angular2-learn目錄,發現多了node_modules文件夾,裡面就是剛剛通過npm install安裝的依賴。
package.json的dependencies欄位下也多了很多記錄,這是--save參數的作用:如果沒有--save參數,這些文件依舊會被下載下來,但是不會被保存到package.json中。
--registry參數用於指定npm的鏡像倉庫,由於網路被牆,不使用--registry很多時候都無法完成下載;registry.npm.taobao.org是阿裡給廣大前端同學的福利,長期維護且高速穩定,在這裡順便也向阿裡的同學說一聲謝謝 :)
3. 安裝gulp和其他開發依賴
在上一步中,我們下載了angular2框架,angular2框架是程式運行時所依賴的。但是我們在編譯時還需要一些其他的類庫或框架支持。
首先是gulp,我們要靠它完成編譯流程的自動化,比如檢測文件修改、自動編譯、生成目標js等。
我們還需要browserify,因為angular2和大多數托管在npm的類庫遵循的都是commonjs標準而非amd標準,它們適用於nodejs環境而非瀏覽器環境。browserify,正如它的名字所暗示的一樣,可以幫助我們把符合commonjs標準的代碼“瀏覽器化”,使之成為符合amd標準的代碼。
當然我們也需要typescript編譯器。博主本來想用非模塊化的angular2框架以及原生javascript來為大家演示angular2 hello world的例子,但是嘗試後發現,這樣做雖然看起來簡單一些,但實在是味同嚼蠟——缺少了模塊化和元信息註解的支持,angular2的韻味一下就掉了一大半。因此這裡我們還是堅持用typescript作為編程語言,可以預見的是,angular2團隊將來會把大部分精力花在typescript的相關支持上,typescript一定是angular2的預設推薦語言。對於不熟悉typescript的同學,其實也不用擔心,typescript是javascript的超集,所有合法的javascript代碼都是合法的typescript代碼,只要面向對象的基礎比較扎實,學習起來會有一脈相承的感覺,曲線不會太陡峭。
首先來安裝gulp
npm install -g gulp --registry=https://registry.npm.taobao.org
註意,這一步中我們使用了-g而非--save參數。這是因為gulp安裝在全局,並不是為某個特定項目安裝的,g指的就是global。Linux和Mac環境下的讀者在這一步時請註意讀寫許可權問題。
然後回到angular2-learn文件夾,為項目安裝編譯時依賴:
npm install --save-dev gulp gulp-browserify gulp-typescript --registry=https://registry.npm.taobao.org
這裡為什麼使用--save-dev而非--save呢?因為這些工具只在腳本編譯的時候使用,最終生成的js並沒有它們的身影,因此只能算作開發依賴。
安裝完成後檢查package.json,發現剛剛的幾個工具都被加到了devDependencies欄位。
到這裡我們就完成了hello world例子所依賴的類庫的安裝。
4. 配置gulpfile.js
正如上文所提到的,我們使用gulp的目的是搭建自動化的編譯環境,使之產出可以在瀏覽器中運行的js代碼。更詳細地說,我們需要gulp檢測ts(typescript腳本的預設尾碼名)文件的變化,一旦有變化發生(比如新建、修改、刪除等),就使用gulp-typescript將腳本從typescript編譯為javascript;由於angular2本身遵循的是適用於nodejs的commonjs標準,從typescript編譯為javascript腳本後,我們還需要使用browserify將它轉變為符合amd標準的javascript文件。流程見下圖:
按照這個思路,我們可以寫出如下gulpfile.js,並將其放到angular2-learn目錄下:
var gulp = require('gulp'); var typescript = require('gulp-typescript'); var browserify = require('gulp-browserify'); gulp.task('compile', function () { gulp.src('app/**.ts') // typescript編譯配置信息 .pipe(typescript({ target: 'es6', module: 'commonjs', moduleResolution: 'node', sourceMap: true, emitDecoratorMetadata: true, experimentalDecorators: true, removeComments: false, noImplicitAny: false })) .on('error', function () { console.log('unhandled error from typescript compilation'); }) // 將typescript編譯後的javascript文件存放到out目錄下 .pipe(gulp.dest('./out')) .on('end', function () { console.log('typescript compilation done'); // 將main.js作為入口文件,使用browserify按amd規範進行合併 gulp.src('out/main.js') .pipe(browserify()) .on('error', function (error) { console.log('unhandled error from browserify resolution'); console.log(error); }) // 將browserify後的文件放到項目根目錄 .pipe(gulp.dest('./')) .on('end', function () { console.log('browserify work done'); }); }); }); gulp.task('default', ['compile'], function () { gulp.watch('app/**.ts', ['compile']); });
不熟悉gulp的同學也不用緊張,這個配置文件其實就是為了表達出上圖中的編譯流程。另外,在typescript編譯配置部分,這裡除了將target設為es6和官網不同外,其餘配置都是使用的angular2推薦的配置。為什麼要編譯到es6而不是es5呢,因為es6的大多數特性新版的瀏覽器已經支持了,而且我們的hello world本身也沒有必要考慮瀏覽器相容性,直接使用es6更為方便。
gulpfile配置好之後,對angular2編譯環境的配置到這裡就結束了。
5. 入口組件app.component.ts
接下來我們就進入到angular2 hello world的編碼部分。作為一門新的前端框架,angular2吸收了很多前端社區近年來的發展成果,比如我們馬上就要看到的基於es6的模塊化開發和typescript提供的裝飾器(decorator)。typescript雖然是javascript的超集,但其語言特性並不是天馬行空的,大多數還是javascript正常發展的自然延續,例如裝飾器將來就有很大希望成為新的ecmascript標準的一部分。
好了回到正題。我們先在angular2-learn根目錄下創建app文件夾,作為存放所有typescript腳本的目錄。
然後在app文件夾中創建app.component.ts文件,用於入口組件的定義。
在組件的定義中,我們需要使用到@angular/core模塊中的Component裝飾器。
import {Component} from '@angular/core';
有Java開發經驗的同學會發現裝飾器的使用和註解(annotation)的使用方法十分相似。在Component裝飾器中,我們需要定義兩個屬性,selector和template。selector的作用在於告知angular要在哪些元素上使用這個組件,它本身類似於css選擇器。template是Component對應的模板,在本篇教程可以暫時認為它是html代碼的容器。
@Component({
selector: 'hello-world',
template: '<h1>hello world</h1>'
})
最後是AppComponent類的定義。本節中暫時還不需要為其添加額外的屬性,但是作為模塊化開發的一部分,為了將該類暴露給其他的文件,我們需要在類的定義前加上export關鍵字。
export class AppComponent {}
app.component.ts的完整文件內容:
// 從@angular/core中引入Component import {Component} from '@angular/core'; // Component裝飾器的使用 @Component({ // selector告知angular在哪裡初始化AppComponent這個組件 selector: 'hello-world', // AppComponent組件的具體模板 template: '<h1>hello world</h1>' }) // 定義AppComponent類並將其暴露給外部環境 export class AppComponent {};
6. 啟動腳本main.ts
上文中,AppComponent類的定義已經完成了,我們還需要一個類似於C語言main函數或者Java靜態main方法的啟動器來開始angular2的初始化。
依然在app目錄下,這次創建main.ts文件。
angular2在瀏覽器中的啟動依賴於定義在@angular/platform-dynamic-browser上的bootstrap函數。為什麼要將bootstrap定義在這樣一個模塊中而非@angular/core中呢,難道啟動不是核心功能嗎?事實上,angular2這樣做是為瞭解耦框架的核心運行時邏輯和初始化邏輯,方便將來後端、原生移動端上的載入和初始化。
除此以外,我們還需要引入剛剛定義的AppComponent。
main.ts的完整文件內容:
// 從@angular/platform-browser-dynamic引入bootstrap函數 import {bootstrap} from '@angular/platform-browser-dynamic'; // 從app.component.ts引入AppComponent import {AppComponent} from './app.component'; // 調用bootstrap函數,並將AppComponent作為參數,即入口組件 bootstrap(AppComponent);
上面的代碼中有一個小地方需要註意,引入AppComponent時,from './app.component'中不能寫./app.component.ts,.ts的尾碼必須被省略掉。否則在typescript的編譯完成後,browserify就找不到相應的類而只能拋出異常了。
7. 創建index.html
html!終於來到了本篇教程的最後一個編碼步驟!
由於angular2的部分運行時依賴我們沒有通過gulp打包進去,這裡需要通過cdn載入,當然不嫌麻煩的同學也可以下載到本地。
body里需要放入hello-world標簽,和AppComponent中selector屬性所定義的hello-world相對應。
最後引入gulp編譯、打包結束後生成的在angular2-learn根目錄下的main.js。
index.html的完整內容:
<!doctype html> <html> <head> <meta charset="utf-8" /> <title>angular2-learn</title> <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" /> <script src="http://cdn.bootcss.com/reflect-metadata/0.1.3/Reflect.min.js"></script> <script src="http://cdn.bootcss.com/angular.js/2.0.0-beta.17/Rx.umd.min.js"></script> <script src="http://cdn.bootcss.com/zone.js/0.6.12/zone.min.js"></script> </head> <body> <hello-world></hello-world> <script src="main.js"></script> </body> </html>
8. 運行gulp併在瀏覽器中打開
上面的步驟完成後,angular2-learn目錄下的結構應該如圖所示:
在命令行中回到angular2-learn目錄,輸入如下命令並回車:
gulp
如果上面的代碼沒有錯誤,應該可以看見
typescript compilation done
browserify work done
的提示,並且angular2-learn目錄下多了out文件夾和main.js。此時在瀏覽器中打開本地index.html,應該就可以看到最終的效果了。
因為gulp監視了ts文件的變動,如果之後要修改和調試,只需要在browserify work done的提示出來之後刷新網頁即可。
angular2 hello world的例子以及隨後的教程所需要的自動化編譯環境就搭建完啦,對angular2是不是多多少少有了點感覺呢?歡迎繼續閱讀本系列的後續教程。