使用 AngularJS 開(kāi)發(fā)一個(gè)大規(guī)模的單頁(yè)應(yīng)用(SPA)
下載源代碼
介紹
(SPA)這樣一個(gè)名字里面蘊(yùn)含著什么呢? 如果你是經(jīng)典的Seinfeld電視秀的粉絲,那么你一定知道Donna Chang這個(gè)名字。Jerry跟Donna見(jiàn)面,Donna其實(shí)不是華人,但是卻因在談?wù)撈鋵?duì)中國(guó)的固有印象比如在針灸上的興趣,以及偶然的一次單詞發(fā)音帶上了點(diǎn)兒中文口音,她將自己末尾的名字縮成了Chang Donna 在電話上同George的母親交談,(通過(guò)引用孔子)給她提了些建議。當(dāng)George向自己的父母介紹Donna是,George的母親意識(shí)到Donna并不是華人,因此并沒(méi)有接受Donna的建議.
單頁(yè)面引用 (SPA), 被定義成一個(gè)目的在于提供一種接近桌面應(yīng)用程序的流暢用戶體驗(yàn)單web頁(yè)面應(yīng)用程序,或者說(shuō)網(wǎng)站. 在一個(gè)SPA中, 所有必需的代碼 – HTML, JavaScript, 以及 CSS – 都是在單頁(yè)面加載的時(shí)候獲取,或者相關(guān)的資源被動(dòng)態(tài)的加載并按需添加到頁(yè)面中, 這常常是在響應(yīng)用戶動(dòng)作的時(shí)候發(fā)生的. 盡管現(xiàn)代的Web技術(shù)(比如那些在HTML5中引入的技術(shù))提供了應(yīng)用程序中各自獨(dú)立的邏輯頁(yè)面相互感知和導(dǎo)航的能力,頁(yè)面卻不會(huì)在過(guò)程中重新加載任何端點(diǎn),或者將控制轉(zhuǎn)到另外一個(gè)頁(yè)面. 同單頁(yè)面應(yīng)用程序的交互常常設(shè)計(jì)到同位于后臺(tái)的web服務(wù)器的動(dòng)態(tài)交互.
那么拿這項(xiàng)技術(shù)同 ASP.NET 的母版頁(yè)Master Pages相比呢? 誠(chéng)然 ASP.NET 的母版頁(yè)讓你可以為自己應(yīng)用程序里的頁(yè)面創(chuàng)建一個(gè)一直的布局。一個(gè)單獨(dú)的母版頁(yè)就可以定義好你想要在整個(gè)應(yīng)用程序中的所有頁(yè)面(或者一組頁(yè)面)上應(yīng)用的外觀和標(biāo)準(zhǔn)動(dòng)作. 然后你就可以再來(lái)創(chuàng)建你想要展示的內(nèi)容各自獨(dú)立頁(yè)面. 當(dāng)用戶發(fā)起對(duì)內(nèi)容頁(yè)面的請(qǐng)求時(shí),它們會(huì)將來(lái)自母版頁(yè)的布局和來(lái)自內(nèi)容頁(yè)面的內(nèi)容混合到一起,產(chǎn)生輸出.
當(dāng)你深入研究SPA和ASP.NET母版頁(yè)實(shí)現(xiàn)這兩者之間的不同時(shí),你就開(kāi)始會(huì)意識(shí)到它們之間相同的地方多于不同的地方——那就是SPA可以看做是一個(gè)簡(jiǎn)單的裝著內(nèi)容頁(yè)面的外殼頁(yè)面,就像是一個(gè)母版頁(yè), 只是SPA中的外殼頁(yè)面不能像母版頁(yè)那樣根據(jù)每一個(gè)新的頁(yè)面請(qǐng)求來(lái)重新裝載和執(zhí)行.
也許“單頁(yè)面應(yīng)用”是個(gè)不幸運(yùn)的名字(像唐娜`程一樣),讓你相信這個(gè)技術(shù)不適合開(kāi)發(fā)需要拓展到企業(yè)級(jí),可能 包含上百頁(yè)面以及數(shù)千用戶的Web應(yīng)用。
本文的目標(biāo)是基于單頁(yè)面應(yīng)用程序開(kāi)發(fā)出擁有數(shù)百頁(yè)的內(nèi)容,包括認(rèn)證,授權(quán),會(huì)話狀態(tài)等功能,可以支持上千個(gè)用戶的企業(yè)級(jí)應(yīng)用。
AngularJS - 概述
本文的樣例包含的功能有創(chuàng)建/跟新用戶賬號(hào),創(chuàng)建/更新客戶和產(chǎn)品。而且,它還允許用戶針對(duì)所有信息執(zhí)行查詢,創(chuàng)建和跟新銷(xiāo)售訂單。為了實(shí)現(xiàn)這些功能,該樣例將會(huì)基于AngularJS來(lái)開(kāi)發(fā)。 AngularJS 是一個(gè)由Google和AngularJS社區(qū)的開(kāi)發(fā)人員維護(hù)的開(kāi)源的Web應(yīng)用框架。
AngularJS僅需HTML,CSS和JavaScript就可在客戶端創(chuàng)建單頁(yè)面應(yīng)用。它的目標(biāo)是是開(kāi)發(fā)和測(cè)試更容易,增強(qiáng)MVC Web應(yīng)用的性能。
這個(gè)庫(kù)讀取HTML中包含的其他定制的標(biāo)簽屬性;然后服從這個(gè)定制的屬性的指令,把頁(yè)面的I/O結(jié)合到有標(biāo)準(zhǔn)JavaScript變量生成的模塊中。這些JavaScript標(biāo)準(zhǔn)變量的值可以手動(dòng)設(shè)置,或者從靜態(tài)或動(dòng)態(tài)的JSON數(shù)據(jù)源中獲取。

AngularJS使用入門(mén) - 外殼頁(yè)面,模塊和路由
 
你首先要做的一件事情就是講AngularJS框架下載到你的項(xiàng)目中,你可以從 https://angularjs.org 獲得框架. 本文的示例程序是使用MS Visual Studio Web Express 2013 Edition開(kāi)發(fā)的,因此我是使用如下的命令從一個(gè)Nuget包安裝AngularJS的:
Install-Package AngularJS -Version 1.2.21
在Nuget包管理控制臺(tái)上. 為了保持簡(jiǎn)單和靈活性,我創(chuàng)建了一個(gè)空的 Visual Studio web 應(yīng)用程序項(xiàng)目,并將Microsoft Web API 2庫(kù)選進(jìn)了核心引用. 這個(gè)應(yīng)用程序?qū)⑹褂肳eb API 2 庫(kù)來(lái)實(shí)現(xiàn) RESTful API 的服務(wù)器端請(qǐng)求.
現(xiàn)在當(dāng)你要使用AngularJS創(chuàng)建一個(gè)SPA應(yīng)用程序是,首先要做的兩件事情就是設(shè)置一個(gè)外殼頁(yè)面,以及用于獲取內(nèi)容頁(yè)面的路由表. 開(kāi)始的時(shí)候,外殼頁(yè)面只需要一個(gè)隊(duì)AngularJS JavaScript庫(kù)的引用,還有一個(gè)ng-view,來(lái)告訴AngularJS內(nèi)容頁(yè)面需要在外殼頁(yè)面的那個(gè)地方被渲染.
- <!DOCTYPE html>
 - <html lang="en">
 - <head>
 - <title>AngularJS Shell Page example</title>
 - </head>
 - <body>
 - <div>
 - <ul>
 - <li><a href="#Customers/AddNewCustomer">Add New Customer</a></li>
 - <li><a href="#Customers/CustomerInquiry">Show Customers</a></li>
 - </ul>
 - </div>
 - <!-- ng-view directive to tell AngularJS where to inject content pages -->
 - <div ng-view></div>
 - <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
 - <script src="app.js"></script>
 - </body>
 - </html>
 
在上面的外殼頁(yè)面示例中,幾個(gè)鏈接唄映射到了AngularJS的路由。div標(biāo)簽上的ng-view指令是一個(gè)能將選定路由的被渲染內(nèi)容頁(yè)面包含到外殼頁(yè)面來(lái)補(bǔ)充AngularJS的$route服務(wù)的指令. 每次當(dāng)目前的路由變化時(shí),包含的視圖也會(huì)根據(jù)$route服務(wù)的配置隨之改變. 比如,當(dāng)用戶選擇了 "Add New Customer" 鏈接,AngularJS 就會(huì)在ng-view所在的div里面渲染用于添加一個(gè)新顧客的內(nèi)容 . 被渲染的內(nèi)容是一個(gè)HTML片段.
下來(lái)的app.js文件同樣也被外殼頁(yè)面引用了。這個(gè)文件里的JavaScript將會(huì)為應(yīng)用程序創(chuàng)建AngularJS模塊。此外,應(yīng)用程序所有的路由配置也會(huì)在這個(gè)文件中定義。你可以把一個(gè)AngularJS模塊想象成封裝你應(yīng)用程序不同部分的容器。大多數(shù)的應(yīng)用程序都會(huì)有一個(gè)主方法,用來(lái)初始化應(yīng)用程序的不同部分,并將它們聯(lián)系起來(lái)。AngularJS應(yīng)用程序卻沒(méi)有一個(gè)主方法,而是讓模塊聲明性的指定應(yīng)用程序如何啟動(dòng)和配置. 本文的示例程序?qū)⒅粫?huì)有一個(gè)AngularJS模塊,雖然應(yīng)用程序中存在幾個(gè)明顯不同的部分(顧客,產(chǎn)品,訂單和用戶).
現(xiàn)在,app.js的主要目的就是如下所示,用來(lái)設(shè)置AngularJS的路由。AngularJS的$routeProvider服務(wù)會(huì)接受 when() 方法,它將為一個(gè)Uri匹配一個(gè)模式. 當(dāng)發(fā)現(xiàn)一次匹配時(shí),獨(dú)立頁(yè)面的HTML內(nèi)容會(huì)跟隨相關(guān)內(nèi)容的控制器文件一同被加載到外殼頁(yè)面中. 控制器文件就簡(jiǎn)單的只是一個(gè)JavaScript文件,它將獲得帶有某個(gè)特定路由請(qǐng)求內(nèi)容的引用.
- //Define an angular module for our app
 - var sampleApp = angular.module('sampleApp', []);
 - //Define Routing for the application
 - sampleApp.config(['$routeProvider',
 - function($routeProvider) {
 - $routeProvider.
 - when('/Customers/AddNewCustomer', {
 - templateUrl: 'Customers/AddNewCustomer.html',
 - controller: 'AddNewCustomerController'
 - }).
 - when('/Customers/CustomerInquiry', {
 - templateUrl: 'Customers/CustomerInquiry.html',
 - controller: 'CustomerInquiryController'
 - }).
 - otherwise({
 - redirectTo: '/Customers/AddNewCustomer'
 - });
 - }]);
 
AngularJS 的控制器
 
AngularJS 控制器無(wú)非就是一個(gè)原生的JavaScript函數(shù),只是被綁定到了一個(gè)特定的范圍而已??刂破饔脕?lái)將邏輯添加到你的視圖。視圖就是HTML頁(yè)面。這些頁(yè)面只是做簡(jiǎn)單的數(shù)據(jù)展示工作,我們會(huì)使用雙向數(shù)據(jù)綁定來(lái)將數(shù)據(jù)綁定到這些HTML頁(yè)面上. 將模型(也就是數(shù)據(jù))同數(shù)據(jù)粘合起來(lái)基本山就是控制器的職責(zé)了.
- <div ng-controller="customerController">
 - <input ng-model="FirstName" type="text" style="width: 300px" />
 - <input ng-model="LastName" type="text" style="width: 300px" />
 - <div>
 - <button class="btn btn-primary btn-large" ng-click="createCustomer()"/>Create</button>
 
對(duì)于上面的AddCustomer模板,ng-controller指令將會(huì)引用JavaScript函數(shù)customerController,這個(gè)控制會(huì)執(zhí)行所有的數(shù)據(jù)綁定以及針對(duì)該視圖的JavaScript函數(shù).
- function customerController($scope)
 - {
 - $scope.FirstName = "William";
 - $scope.LastName = "Gates";
 - $scope.createCustomer = function () {
 - var customer = $scope.createCustomerObject();
 - customerService.createCustomer(customer,
 - $scope.createCustomerCompleted,
 - $scope.createCustomerError);
 - }
 - }
 
開(kāi)箱即用 - 可擴(kuò)展性問(wèn)題
 
當(dāng)我為本文開(kāi)發(fā)這個(gè)實(shí)力程序時(shí),首當(dāng)其沖的兩個(gè)擴(kuò)展性問(wèn)題在應(yīng)用單頁(yè)面應(yīng)用程序時(shí)變得明顯起來(lái)。其實(shí)一個(gè)開(kāi)箱即用,AngularJS需要應(yīng)用程序的外殼頁(yè)面中所有的JavaScript文件和控制器在啟動(dòng)中伴隨應(yīng)用程序的啟動(dòng)被引入和下載. 對(duì)于一個(gè)大型的應(yīng)用程序而言,可能會(huì)有上百個(gè)JavaScript文件,這樣情況看上去就會(huì)不怎么理想。我遇到的另外一個(gè)問(wèn)題就是AngularJS的路由表。我找到的所有示例都有針對(duì)所有內(nèi)容的所有路由的硬編碼。而我想要的確不是一個(gè)在路由表里包含上百項(xiàng)路由記錄的方案.
英文原文:Developing a Large Scale Application with a Single Page Application (SPA) using AngularJS
譯文鏈接:http://www.oschina.net/translate/developing-a-large-scale-application-with-a-single















 
 
 











 
 
 
 