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