zoukankan      html  css  js  c++  java
  • 前端进化史(二)

    接上篇:

    目前来说,引入前端框架已经是大势所趋了,很多时候后端的一些数据处理都转移给了前端去完成,特别是在REST模式下。

    下面部分来自segment社区的内容摘选:

    什么是前端框架?引入前端框架的契机是什么?

    当前端从web page变成web app,就需要前端框架了,web page 以表现为主,web app以应用为主。现在我们在 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...)

  • 相关阅读:
    MySQL DDL 在指定位置新增字段
    .NET平台常见技术框架整理汇总
    使用Linq求和方法Sum计算集合中多个元素和时应该注意的性能问题
    python时间操作
    django 利用原生sql操作数据库
    滑动验证码获取像素偏移量
    python opencv简单使用
    django通过旧数据库库生成models
    pandas 操作csv文件
    简单工厂模式(Simple Factory Pattern)
  • 原文地址:https://www.cnblogs.com/brancepeng/p/5387022.html
Copyright © 2011-2022 走看看