接上篇:javascript
目前來講,引入前端框架已是大勢所趨了,不少時候後端的一些數據處理都轉移給了前端去完成,特別是在REST模式下。html
下面部分來自segment社區的內容摘選:前端
什麼是前端框架?引入前端框架的契機是什麼?java
當前端從web page變成web app,就須要前端框架了,web page 以表現爲主,web app以應用爲主。如今咱們在 web 上,已經不只僅是去看了,咱們更多的時候是去用。angularjs
前端框架的使用,讓不斷刷新從服務器得到靜態頁面的流程 變成了純粹的客戶端對服務端的請求數據-組織數據-顯示數據的流程 。web
1.數據模型數據庫
在這一塊,我想插入一些面向對象的思想。編程
什麼是面向對象?後端
面向對象編程:簡稱就是OOP(Object Oriented Programming)。它把對象當作程序的基本單元,一個對象包括數據和操做數據的函數。
面向過程的程序設計把計算機程序視爲一系列的命令集合,即一組函數的順序執行。爲了簡化程序設計,面向過程把函數繼續切分爲子函數,即把大塊函數經過切割成小塊函數來下降系統的複雜度。數組
而面向對象的程序設計把計算機程序視爲一組對象的集合,而每一個對象均可以接收其餘對象發過來的消息,並處理這些消息,計算機程序的執行就是一系列消息在各個對象之間傳遞。(總的宗旨就是:你辦事我放心!)這也是一個很好的鑑別一個面向對象的設計是否正確的方法。一個好的面向對象設計,會讓你讓他辦事的時候,你不得不放心(也就是說,你不放心也沒用,反正你什麼都不知道)。
面向對象的設計思想是從天然界中來的,由於在天然界中,類(Class)和實例(Instance)的概念是很天然的。Class是一種抽象概念,好比咱們定義的Class——Student,是指學生這個概念,而實例(Instance)則是一個個具體的Student,好比,Bart Simpson和Lisa Simpson是兩個具體的Student:
因此,面向對象的設計思想是抽象出Class,根據Class建立Instance。
面向對象的抽象程度又比函數要高,由於一個Class既包含數據,又包含操做數據的方法。
這裏產生一個問題:如何理解js中的數據模型?(...後面會介紹)
在這些框架裏,定義數據模型的方式與以往有些差別,主要在於數據的get和set更加有意義了,好比說,能夠把某個實體的get和set綁定到RESTful的服務上,這樣,對某個實體的讀寫能夠更新到數據庫中。另一個特色是,它們通常都提供一個事件,用於監控數據的變化,這個機制使得數據綁定成爲可能。
在一些框架中,數據模型須要在原生的JavaScript類型上作一層封裝,好比Backbone的方式是這樣:
//下面是backboneJs的定義數據模型的方式; var Todo = Backbone.Model.extend({ // Default attributes for the todo item. defaults : function() { return { title : "empty todo...", order : Todos.nextOrder(), done : false }; }, // Ensure that each todo created has `title`. initialize : function() { if (!this.get("title")) { this.set({ "title" : this.defaults().title }); } }, // Toggle the 'done' state of this todo item. toggle : function() { this.save({ done : !this.get("done") }); } });
上述例子中,defaults方法用於提供模型的默認值,initialize方法用於作一些初始化工做,這兩個都是約定的方法,toggle是自定義的,用於保存todo的選中狀態。
數據模型也能夠包含一些方法,好比自身的校驗,或者跟後端的通信、數據的存取等等,在上面例子中,也有體現一些。
AngularJS的模型定義方式與Backbone不一樣,能夠不須要通過一層封裝,直接使用原生的JavaScript簡單數據、對象、數組,相對來講比較簡便。
2.控制器
控制器是模型和視圖之間的紐帶。控制器從視圖得到事件和輸入,對它們進行處理(極可能包含模型),並相應地更新視圖。當頁面加載時,控制器會給視圖添加事件監聽,好比監聽表單提交或按鈕點擊。而後,當用戶和應用產生交互時,控制器中的事件觸發器就開始工做了。很典型的,在controller中定義表單的提交事件或者點擊事件。
下面用Jquery實現一個例子:
var Controller = {}; // 使用匿名函數來封裝一個做用域 (Controller.users = function($){ var nameClick = function(){ /* ... */ }; // 在頁面加載時綁定事件監聽 $(function(){ $("#view .name").click(nameClick); }); })(jQuery);
上面的代碼建立了user控制器,這個控制器是放在controller變量下的命名空間。而後用匿名函數封裝了做用域,防止對全局做用域污染。當頁面加載時,程序給視圖元素綁定了點擊事件的監聽。
再舉個Angularjs中控制器的例子:仍是以上面的todo對象爲例,在AngularJS中,會有一些約定的注入,好比$scope,它是控制器、模型和視圖之間的橋樑。在控制器定義的時候,將$scope做爲參數,而後,就能夠在控制器裏面爲它添加模型的支持。
function TodoCtrl($scope) { $scope.todos = [{ text : 'learn angular', done : true }, { text : 'build an angular app', done : false }]; $scope.addTodo = function() { $scope.todos.push({ text : $scope.todoText, done : false }); $scope.todoText = ''; }; $scope.remaining = function() { var count = 0; angular.forEach($scope.todos, function(todo) { count += todo.done ? 0 : 1; }); return count; }; $scope.archive = function() { var oldTodos = $scope.todos; $scope.todos = []; angular.forEach(oldTodos, function(todo) { if (!todo.done) $scope.todos.push(todo); }); }; }
本例中爲$scope添加了todos這個數組,addTodo,remaining和archive三個方法,而後,能夠在視圖中對他們進行綁定。
3.視圖
對於AngularJS來講,基本不須要有額外的視圖定義,它採用的是直接定義在HTML上的方式,好比:
<div ng-controller="TodoCtrl"> <span>{{remaining()}} of {{todos.length}} remaining</span> <a href="" ng-click="archive()">archive</a> <ul class="unstyled"> <li ng-repeat="todo in todos"> <input type="checkbox" ng-model="todo.done"> <span class="done-{{todo.done}}">{{todo.text}}</span> </li> </ul> <form ng-submit="addTodo()"> <input type="text" ng-model="todoText" size="30" placeholder="add new todo here"> <input class="btn-primary" type="submit" value="add"> </form> </div>
在這個例子中,使用ng-controller注入了一個TodoCtrl的實例,而後,在TodoCtrl的$scope中附加的那些變量和方法均可以直接訪問了。注意到其中的ng-repeat部分,它遍歷了todos數組,而後使用其中的單個todo對象建立了一些HTML元素,把相應的值填到裏面。這種作法和ng-model同樣,都創造了雙向綁定,即:
並且,這種綁定都會自動忽略其中可能由於空數據而引發的異常狀況。
4.模板
模板是這個時期一種很典型的解決方案。來個場景:在一個界面上重複展現相似的DOM片斷,例如微博,
iv class="post" ng-repeat="post in feeds"> <div class="author"> <a ng-href="/user.html?user={{post.creatorName}}">@{{post.creatorName}}</a> </div> <div>{{post.content}}</div> <div> 發佈日期:{{post.postedTime | date:'medium'}} </div> </div>
5.路由
一般路由是定義在後端的,可是在這類MV*框架 的幫助下,路由能夠由前端來解析執行。
AngularJS中定義路由的方式有些區別,它使用一個$routeProvider來提供路由的存取,每個when表達式配置一條路由信息,otherwise配置默認路由,在配置路由的時候,能夠指定一個額外的控制器,用於控制這條路由對應的html界面:
app.config(['$routeProvider', function($routeProvider) { $routeProvider.when('/phones', { templateUrl : 'partials/phone-list.html', controller : PhoneListCtrl }).when('/phones/:phoneId', { templateUrl : 'partials/phone-detail.html', controller : PhoneDetailCtrl }).otherwise({ redirectTo : '/phones' }); }]);
注意,在AngularJS中,路由的template並不是一個完整的html文件,而是其中的一段,文件的頭尾均可以不要,也能夠不要那些包含的外部樣式和JavaScript文件,這些在主界面中載入就能夠了。
6.自定義標籤
最完美的體現這個功能是angularjs.
在AngularJS的首頁,能夠看到這麼一個區塊「Create Components」,在它的演示代碼裏,可以看到相似的一段:
<tabs> <pane title="Localization"> ... </pane> <pane title="Pluralization"> ... </pane> </tabs>
那麼它是怎麼作到的呢?
在angularjs首頁,咱們能夠看到這樣一塊區域「Create Components」,在他的演示代碼中,能夠看到這一段:
<tabs> <pane title="Localization"> ... </pane> <pane title="Pluralization"> ... </pane> </tabs>
那麼它是怎麼作到的呢?緣由在下面:
angular.module("components",[]).directive('tabs',function( ){ return { restrict:'E', transclude :true, scope:{}, controller: function($scope,$element){ var panes = $scope.panes = []; $scope.select = function(pane) { angular.forEach(panes, function(pane) { pane.selected = false; }); pane.selected = true; } this.addPane = function(pane) { if (panes.length == 0) $scope.select(pane); panes.push(pane); } }, template : '<div class="tabbable">' + '<ul class="nav nav-tabs">' + '<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">' + '<a href="" ng-click="select(pane)">{{pane.title}}</a>' + '</li>' + '</ul>' + '<div class="tab-content" ng-transclude></div>' + '</div>', replace : true }; }).directive('pane',function(){ return { require : '^tabs', restrict :'E', transclude :'true', scope :{title : '@'}, link : function(scope,elment,attrs,tabsCtrl){ tabsCtroller.addpane(scope) ; }, template :'<div class="tab-pane" ng-class="{active: selected}" ng- transclude>' + '</div>', replace :true }; });
這段代碼裏,定義了tabs和pane兩個標籤,而且限定了pane標籤不能脫離tabs而單獨存在,tabs的controller定義了它的行爲,二者的template定義了實際生成的html,經過這種方式,開發者能夠擴展出本身須要的新元素,對於使用者而言,這不會增長任何額外的負擔。
以上內容大部分來自圖靈社區。下篇接着本篇最後一個例子續寫directive的各類屬性含義以及controller之間的交互或者說通訊。(寫着寫着不知不覺又到了angularJs...)