讓我們從零開始用JavaScript創(chuàng)建一個(gè)Angular 2應(yīng)用。(教程同時(shí)也有TypeScript版和Dart版瞎惫。)
看一下運(yùn)行效果
運(yùn)行在線案例是最快的看Angular 2應(yīng)用演示的方法骗卜。
點(diǎn)擊鏈接會(huì)打開瀏覽器喊衫,載入plunker,并顯示一個(gè)消息蒸辆。
下面是文件結(jié)構(gòu)
angular2-quickstart
L------app
L------app.component.js
L------boot.js
L------index.html
L------license.md
從功能上說,它就是一個(gè)index.html文件和app/文件夾下的兩個(gè)JavaScript文件摩泪。我們完全可以搞定。
當(dāng)然我們也不會(huì)構(gòu)建許多只能在plunker運(yùn)行的應(yīng)用的劫谅。讓我們跟隨一個(gè)實(shí)際中的流程见坑。
- 搭建開發(fā)環(huán)境
- 寫我們的應(yīng)用的Angular根組件
- 啟動(dòng)它控制主頁面
- 書寫主頁面(index.html)
如果我們按照下面的步驟,忽略解釋捏检,我們完全可以在5分鐘內(nèi)構(gòu)建Quickstart這個(gè)應(yīng)用荞驴。
但大多數(shù)人還是對(duì)why和how比較感興趣的,這個(gè)會(huì)多花一些時(shí)間贯城。
開發(fā)環(huán)境
我們需要一個(gè)立足之地(應(yīng)用的項(xiàng)目文件夾)戴尸,一些庫還有一個(gè)編輯器。
創(chuàng)建一個(gè)項(xiàng)目文件夾
mkdir angular2-quickstart
cd angular2-quickstart
添加我們需要的庫文件
我們推薦使用npm包管理器來獲取和管理我們的開發(fā)庫冤狡。
添加一個(gè)package.json文件到項(xiàng)目文件夾孙蒙。
package.json
{
"name": "angular2-quickstart",
"version": "1.0.0",
"scripts": {
"start": "npm run lite",
"lite": "lite-server"
},
"license": "ISC",
"dependencies": {
"angular2": "2.0.0-beta.0",
"systemjs": "0.19.6",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.3",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.0",
"zone.js": "0.5.10"
},
"devDependencies": {
"lite-server": "^1.3.1"
}
}
想詳細(xì)了解?看下面附錄的解釋悲雳。
通過npm install命令安裝所有包挎峦。遇到紅色的出錯(cuò)信息直接忽略,安裝會(huì)成功的合瓢。詳細(xì)看附錄坦胶。
我們的第一個(gè)Angular組件
組件是最基本的Angular概念。一個(gè)組件管理一個(gè)視圖(視圖也就是我們向用戶展示信息并對(duì)用戶反饋?zhàn)鞒龌貞?yīng)的一塊網(wǎng)頁)
從技術(shù)上講,組件就是一個(gè)控制視圖模板的類顿苇。在創(chuàng)建Angular應(yīng)用的時(shí)候峭咒,我們會(huì)寫許多組件。這是我們第一次嘗試纪岁,很簡(jiǎn)單凑队。
創(chuàng)建一個(gè)應(yīng)用源碼子文件夾
我們要把我們的應(yīng)用代碼放到根目錄下的app/子文件夾中。執(zhí)行下面的命令:
mkdir app
cd app
添加組建文件
添加一個(gè)名為app.component.js的文件:
app/app.component.js
(function(app) {
app.AppComponent =
ng.core.Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));
我們通過鏈?zhǔn)秸{(diào)用全局Angular core命名空間ng.core中的Component和Class方法創(chuàng)建了一個(gè)名為AppComponent的可視化組件幔翰。
app/app.component.js (component schema)
app.AppComponent =
ng.core.Component({
})
.Class({
});
Component方法接受一個(gè)包含兩個(gè)屬性的配置對(duì)象漩氨;Class方法是我們實(shí)現(xiàn)組件本身的地方,在Class方法中我們給組件添加屬性和方法遗增,它們會(huì)綁定到相應(yīng)的視圖和行為叫惊。
模塊
Angular應(yīng)用都是模塊化的。包含的每個(gè)文件都有相應(yīng)的功能做修。
ES5沒有內(nèi)置的模塊化系統(tǒng)霍狰。但第三方提供的模塊系統(tǒng)有幾個(gè)可以使用。但是饰及,為了簡(jiǎn)單并免于為選擇糾結(jié)蔗坯,我們會(huì)為我們的應(yīng)用創(chuàng)建一個(gè)單獨(dú)全局命名空間。
我們把它命名為app旋炒,并且會(huì)把所有的代碼包都添加到這個(gè)全局對(duì)象上步悠。
我們不想用任何東西污染全局命名空間。因此在每個(gè)文件內(nèi)我們都把代碼包裹在一個(gè)IIFE(立即執(zhí)行函數(shù)表達(dá)式)中瘫镇。
app/app.component.js (IIFE)
(function(app) {
})(window.app || (window.app = {}));
我們將全局app命名空間對(duì)象傳入IIFE中鼎兽,如果不存在就用一個(gè)空對(duì)象初始化它。
大部分應(yīng)用文件通過在app命名空間上添加?xùn)|西來輸出代碼铣除,我們?cè)赼pp.component.js文件中輸出了AppComponent谚咬。
app/app.component.js (export)
app.AppComponent =
一個(gè)更加復(fù)雜的應(yīng)用會(huì)有以樹形結(jié)構(gòu)繼承自AppComponent的子組件。一個(gè)更加復(fù)雜的應(yīng)用會(huì)有更多文件和模塊尚粘,至少和它的組件數(shù)一樣多择卦。
Quickstart并不復(fù)雜,我們只需要一個(gè)組件郎嫁。然而即使在這么小的應(yīng)用中模塊依然扮演一個(gè)組織角色秉继。
模塊依賴其他模塊。在JavaScript Angular應(yīng)用中泽铛,當(dāng)我們需要其他組件提供的一些東西時(shí)尚辑,我們從app對(duì)象中獲取它。當(dāng)其他組件需要引用AppComponent盔腔,它用下面方法從app.AppComponent獲取杠茬。
app/boot.js (import)
ng.platform.browser.bootstrap(app.AppComponent);
Angular也是模塊化的月褥,它是一個(gè)庫模塊集合。每一個(gè)庫本身就是一個(gè)由多個(gè)相關(guān)功能模塊構(gòu)成的模塊瓢喉。
當(dāng)我們需要Angular的東西時(shí)宁赤,我們使用ng對(duì)象。
Class定義對(duì)象
文件底部是一個(gè)空的AppComponent的類定義對(duì)象栓票。當(dāng)我們要?jiǎng)?chuàng)建一個(gè)是有實(shí)際意義的應(yīng)用時(shí)决左,我們可以使用屬性和應(yīng)用邏輯來擴(kuò)展這個(gè)對(duì)象。我們的AppComponent類只有一個(gè)空的構(gòu)造函數(shù)逗载,因?yàn)樵赒uickstart這個(gè)例子中我們不需要它做什么哆窿。
app.component.js (class)
.Class({
constructor: function() {}
});
Component定義對(duì)象
ng.core.Component()告訴Angular這個(gè)類定義對(duì)象是一個(gè)Angular組件链烈。傳遞給ng.core.Component()的配置對(duì)象有兩個(gè)字段:selector和template厉斟。
app.component.js (component)
ng.core.Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
selector為一個(gè)宿主HTML元素定義了一個(gè)簡(jiǎn)單的CSS選擇器my-app。當(dāng)Angular在宿主HTML中遇到一個(gè)my-app元素時(shí)它創(chuàng)建并顯示一個(gè)AppComponent實(shí)例强衡。
一定要記住my-app選擇器擦秽,我們?cè)趧?chuàng)建index.html時(shí)需要這個(gè)信息。
template屬性容納著組件的模板漩勤。一個(gè)模板以HTML的形式告訴Angular如何渲染一個(gè)視圖感挥。我們的模板是一行顯示“My First Angular App”的HTML代碼。
現(xiàn)在我們需要一些東西告訴Angular來加載這個(gè)組件越败。
讓它啟動(dòng)
給app/文件夾中添加一個(gè)新文件boot.js
app/boot.js
(function(app) {
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(app.AppComponent);
});
})(window.app || (window.app = {}));
我們需要兩件東西來啟動(dòng)應(yīng)用触幼。
- Angular的browser bootstrap函數(shù)
- 我們剛剛寫的應(yīng)用根組件
我們把他們都放到我們的命名空間下。然后調(diào)用bootstrap究飞,將根組件類型AppComponent作為參數(shù)傳進(jìn)去置谦。
在下面的附錄中學(xué)習(xí)為什么我們需要ng.platform.browser的bootstrap方法以及為什么我們要?jiǎng)?chuàng)建一個(gè)單獨(dú)的boot.js文件。
我們已經(jīng)讓Angular在瀏覽器中用我們?cè)诟康慕M件啟動(dòng)應(yīng)用了亿傅。Angular會(huì)把它放到哪里呢媒峡?
添加index.html
Angular在我們的index.html上的特定位置顯示我們的應(yīng)用。現(xiàn)在是創(chuàng)建它的時(shí)候了葵擎。
index.html
<html>
<head>
<title>Angular 2 QuickStart</title>
<!-- 1. Load libraries -->
<!-- IE required polyfill -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/angular2/bundles/angular2-all.umd.js"></script>
<!-- 2. Load our 'modules' -->
<script src='app/app.component.js'></script>
<script src='app/boot.js'></script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
以上代碼有3部分值得注意:
- 載入我們需要的JavaScript庫谅阿;在下面學(xué)習(xí)它們。
- 載入我們自己的JavaScript文件酬滤,注意它們的順序(boot.js需要app.component.js文件在前面)签餐。
- 我們?cè)?lt;body>標(biāo)簽中添加<my-app>標(biāo)簽。這就是我們的應(yīng)用生活的地方盯串。
當(dāng)Angular在boot.js中調(diào)用bootstrap函數(shù)時(shí)氯檐,它讀取AppComponent的元數(shù)據(jù),找到my-app選擇器嘴脾,定位到一個(gè)名字為my-app的元素男摧,然后再這個(gè)標(biāo)簽之間載入我們的應(yīng)用蔬墩。
運(yùn)行!
打開一個(gè)終端耗拓,輸入以下命令
npm start
這個(gè)命令運(yùn)行了一個(gè)叫做lite-server的靜態(tài)服務(wù)器拇颅。它在瀏覽器中加載index.html頁面并在應(yīng)用文件改變時(shí)刷新瀏覽器。
稍等片刻乔询,瀏覽器標(biāo)簽就會(huì)打開并顯示
恭喜樟插!我們已經(jīng)上手了。
如果你看到顯示的是Loading...查看一下瀏覽器ES2015支持附錄竿刁。
做一些改變
試著將消息改成“我的第二個(gè)Angular 2應(yīng)用”黄锤。
lite-server一直在監(jiān)聽,因此它會(huì)知道這個(gè)改變食拜,并刷新瀏覽器鸵熟,然后顯示修改后的消息。
這是一個(gè)有趣的開發(fā)應(yīng)用的方法负甸。
結(jié)束后關(guān)掉終端窗口就關(guān)掉了服務(wù)器流强。
文件結(jié)構(gòu)
我們最終的文件結(jié)構(gòu)是這樣的:
angular2-quickstart
L------node_modules
L------app
L------app.component.js
L------boot.js
L------index.html
L------package.json
下面是對(duì)應(yīng)的文件代碼。
app/app.component.js
(function(app) {
app.AppComponent =
ng.core.Component({
selector: 'my-app',
template: '<h1>My First Angular 2 App</h1>'
})
.Class({
constructor: function() {}
});
})(window.app || (window.app = {}));
app/boot.js
(function(app) {
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(app.AppComponent);
});
})(window.app || (window.app = {}));
index.html
<html>
<head>
<title>Angular 2 QuickStart</title>
<!-- 1. Load libraries -->
<!-- IE required polyfill -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/angular2/bundles/angular2-all.umd.js"></script>
<!-- 2. Load our 'modules' -->
<script src='app/app.component.js'></script>
<script src='app/boot.js'></script>
</head>
<!-- 3. Display the application -->
<body>
<my-app>Loading...</my-app>
</body>
</html>
package.json
{
"name": "angular2-quickstart",
"version": "1.0.0",
"scripts": {
"start": "npm run lite",
"lite": "lite-server"
},
"license": "ISC",
"dependencies": {
"angular2": "2.0.0-beta.0",
"systemjs": "0.19.6",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.3",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.0",
"zone.js": "0.5.10"
},
"devDependencies": {
"lite-server": "^1.3.1"
}
}
打包
我們的第一個(gè)應(yīng)用并沒有做太多事情呻待。它基本上就是Angular 2版本的“Hello World”打月。
我們?cè)诘谝淮螄L試中盡量簡(jiǎn)單:我們寫了一個(gè)小Angular組件,向index.html添加一些JavaScript庫蚕捉,并啟動(dòng)一個(gè)靜態(tài)文件服務(wù)器奏篙。這就是我們對(duì)一個(gè)“Hello World”的期望吧。
我們有更遠(yuǎn)大的目標(biāo)迫淹。
好消息是我們已經(jīng)入門了秘通。我們可能僅僅碰了一下package.json文件來更新庫。添加我們的應(yīng)用模塊文件之后千绪,我們僅僅在需要添加庫和樣式表時(shí)打開index.html充易。
我們要進(jìn)行下一步學(xué)習(xí),并使用Angular 2創(chuàng)建一個(gè)有很棒功能的小應(yīng)用荸型。
加入我們的英雄教程之旅吧盹靴!
附錄
附錄的作用就是對(duì)上面一些簡(jiǎn)單略過的點(diǎn)詳細(xì)講解。
這里沒有必要的資料瑞妇。如果好奇可以往下看稿静。
附錄:庫
我們載入下面的腳本:
index.html
<!-- IE required polyfill -->
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/angular2/bundles/angular2-all.umd.js"></script>
我們首先載入了一個(gè)IE polyfill。IE需要一個(gè)polyfill來運(yùn)行那些依賴ES2015 promise和動(dòng)態(tài)模塊加載的應(yīng)用辕狰。大部分應(yīng)用都需要這些功能改备,而且大部分應(yīng)用都能在IE中運(yùn)行。
然后是Angular 2的polyfill蔓倍,angular2-polyfills.js悬钳,再然后是Reactive Extensions RxJS庫盐捷。
我們的Quickstart并不使用Reactive Extensions,但后面的實(shí)用的應(yīng)用要用到它默勾。所以我們提前加入碉渡。
最后我們加載Angular 2的網(wǎng)絡(luò)開發(fā)版本。
當(dāng)我們經(jīng)驗(yàn)豐富并對(duì)加載時(shí)間內(nèi)存使用等產(chǎn)品性能比較關(guān)注時(shí)我們可以選擇不同的庫母剥。
附錄:package.json
npm是一個(gè)流行的包管理器滞诺,Angular應(yīng)用開發(fā)者依靠它來獲取和關(guān)系應(yīng)用需要的庫。
我們?cè)谝粋€(gè)npm package.json文件中指明我們需要的包环疼。
Angular團(tuán)隊(duì)建議我們使用在dependencies和devDependencies部分列出的包习霹。
package.json (dependencies)
{
"dependencies": {
"angular2": "2.0.0-beta.0",
"systemjs": "0.19.6",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.3",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.0",
"zone.js": "0.5.10"
},
"devDependencies": {
"lite-server": "^1.3.1"
}
}
也可以選擇其他包。我們推薦這些包是因?yàn)檫@些在一起工作的很好§帕ィ現(xiàn)在和我們一起搖擺吧淋叶。之后你可以根據(jù)自己的愛好和經(jīng)驗(yàn)隨意替換。
package.json文件有一個(gè)可選的scripts部分等限,在這里我們可以定義一些幫助命令來執(zhí)行開發(fā)和構(gòu)建任務(wù)爸吮。我們已經(jīng)在我們的package.json文件中包含了一些這樣的scripts芬膝。
package.json (scripts)
{
"scripts": {
"start": "npm run lite",
"lite": "lite-server"
}
}
我們已經(jīng)知道使用<code>npm start</code>來運(yùn)行服務(wù)器望门。
我們指定的是<code>npm start</code>命令,但實(shí)際運(yùn)行的是<code>npm run lite</code>命令锰霜。
我們用如下形式執(zhí)行npm scripts:
npm run + script-name
<code>npm run lite</code>的作用-運(yùn)行l(wèi)ite-server筹误,它是John Papa書寫并維護(hù)的一個(gè)輕量的靜態(tài)文件服務(wù)器,對(duì)使用路由的Angular應(yīng)用支持很好癣缅。
附錄:npm錯(cuò)誤和警告
如果在執(zhí)行<code>npm install</code>命令時(shí)沒有出現(xiàn)<code>npm ERR!</code>厨剪,一起都好∮汛妫可能會(huì)有幾個(gè)<code>npm WARN</code>消息祷膳,這也沒有影響。
我們會(huì)經(jīng)常在一連串的<code>gyp ERR!</code>(gyp: generate your project)消息后看到一個(gè)<code>npm WARN</code>消息屡立。不用管他們直晨。一個(gè)包會(huì)使用<code>node-gyp</code>重新編譯自己。如果重新編譯失敗膨俐,包會(huì)恢復(fù)(場(chǎng)使用一個(gè)預(yù)編譯版本)勇皇,一切正常工作。
只要保證在<code>npm install</code>命令最后面沒有<code>npm ERR!</code>就好焚刺。
附錄:boot.js
啟動(dòng)是平臺(tái)相關(guān)的敛摘。
我們使用的bootstrap函數(shù)來自ng.platform.browser而不是ng.core。這是有原因的乳愉。
創(chuàng)建一個(gè)單獨(dú)boot.js的原因:
- 這是容易的
分離boot.js很容易兄淫。這對(duì)大多數(shù)應(yīng)用都有益處屯远,雖然在Quickstart中沒什么用。讓我們從最開始就養(yǎng)成好習(xí)慣捕虽。 - 可測(cè)試性
即使我們不用測(cè)試Quickstart我們也因該總一開始就考慮可測(cè)試性氓润。
如果同一個(gè)文件中有一個(gè)對(duì)bootstrap的調(diào)用,要測(cè)試這個(gè)組件就比較困難薯鳍。只要我們載入組件文件來測(cè)試組件咖气,bootstrap函數(shù)就會(huì)嘗試著在瀏覽器中載入應(yīng)用。它會(huì)拋出一個(gè)錯(cuò)誤挖滤,因?yàn)槲覀冎幌霚y(cè)試組件而不是運(yùn)行整個(gè)應(yīng)用崩溪。
在boot.js文件中找到bootstrap函數(shù),刪除這個(gè)似是而非的錯(cuò)誤斩松,留下一個(gè)干凈的組件模塊文件伶唯。 - 重用性
在應(yīng)用的演化過程中,我們要重構(gòu)惧盹、重命名和重定位文件乳幸。當(dāng)文件調(diào)用bootstrap時(shí)我們對(duì)這些文件不能做上面任何操作。我們不能移動(dòng)钧椰,不能在其他應(yīng)用中重用組件粹断。我們不能為提升性能在服務(wù)器端預(yù)渲染組件。 - 關(guān)注分離
一個(gè)組件的責(zé)任就是展示和管理一個(gè)視圖嫡霞。
啟動(dòng)應(yīng)用和視圖管理沒有一點(diǎn)關(guān)系瓶埋。這是一個(gè)分離的關(guān)注點(diǎn)。我們?cè)跍y(cè)試和重用中遇到的很多問題都源于這種責(zé)任的混淆诊沪。 - 引入/輸出
當(dāng)分離boot.js時(shí)我們學(xué)會(huì)了Angular一個(gè)關(guān)鍵技能:如何通過簡(jiǎn)單的命名空間抽象輸出一個(gè)模塊和如何導(dǎo)入另一個(gè)模塊养筒。在后續(xù)學(xué)習(xí)中會(huì)經(jīng)常這么做的。