前端進化史(二)

接上篇: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...)

相關文章
相關標籤/搜索