今天进入第二个部分:控制器。

控制器和状态

从以往的开发经验来看。我们都是将状态保存在server的session或者本地cookie中,但Javascript应用往往被限制在单页面,所以我们也能够将状态保存在client的内存里面。保存在内存还意味着能带来更快的界面响应。

在MVC里面,状态都是保存在控制器里的,控制器相当于应用中视图和模型的纽带。

当载入页面的时候,控制器将事件处理程序绑定在视图里。并适时处理回调,以及和模型必要的对接。

控制器是模块化的并且非常独立,理想状况下不应该定义不论什么全局变量,而是应该定义为全然解耦的功能组件。

所以我们通过模块模式将控制器放在一个马上处理的匿名函数里并传递全局參数进去,避免在内部訪问全局变量时须要遍历作用域,也清晰地表明了这个模块使用了哪些全局变量。

(function($){
var mod = {}; //改变參数的上下文并马上运行
mod.load = function(func){
$($.proxy(func, this));
}; //点击事件
mod.assetsClick = function(){
//处理点击
console.log("click!");
} //调用load方法
mod.load(function(){
//为元素加入单击事件
this.view = $("#view");
this.view.find(".assets").click(
$.proxy(this.assetsClick, this)
);
});
})(jQuery);

假设页面内有例如以下元素

<div id="view">
<button class="assets">button</button>
</div>

单击button后。将输出”click”,这也暗示了控制器和视图的联系。事实上书的这一部分讲的就是怎样在控制器内保存状态,依据状态的不同改变不同的视图。

(注)jQuery.proxy(method, context)

$.proxy方法是jQuery中的代理方法,它接收两个參数。返回一个新method,该方法始终保持context上下文。

比方在button的监听里:

$("#btn").click(function(){
//这里的this代表button
});
$("#btn").click(
$.proxy(function(){
//这里的this代表window
}, window);
);

(注解完)

抽象出库

如今将控制器抽象成库,并加入一些新的方法。这样就能在外部或别的模块中重用它了。

(function($, exports){
//假设通过构造方法传递參数,则调用include方法
var mod = function(includes){
if(includes){
this.include(includes);
}
};
//改变原型对象名(便于调用)
mod.fn = mod.prototype; //定义自己的代理方法。上下文始终指向控制器自己
mod.fn.proxy = function(func){
return $.proxy(func, this);
}; //马上运行函数
mod.fn.load = function(func){
$(this.proxy(func));
}; //为构造器加入方法
mod.fn.include = function(ob){
$.extend(this, ob);
}; //将构造器暴露出全局,在外界也能够訪问
exports.Controller = mod;
})(jQuery, window);

在别的地方须要用到控制器的时候,就调用Controller就可以:

(function($, Controller){
//创建控制器
var mod = new Controller(); //改变view的类名
mod.toggleClass = function(e){
//jQuery中的toggleClass方法代表假设存在此类名则删除,不存在则加入
this.view.toggleClass("over");
} //页面载入完马上运行
mod.load(function(){
//绑定页面元素,加入监听
this.view = $("#view");
this.view.mouseover(this.proxy(this.toggleClass));
this.view.mouseout(this.proxy(this.toggleClass));
});
})(jQuery, Controller)

在以上的使用中,通过匿名函数马上调用的方法并非在DOM载入之后载入的。而是在生成DOM之前,然而控制器的load方法又是在页面文档载入完毕之后才进行回调。我们能够对控制器进行进一步改写,在DOM生成之后统一载入控制器。

//定义一个全局对象
var exports = this;
(function($){
var mod = {};
//提供一个create方法,生成控制器
mod.create = function(includes){
//create方法返回的就是result,一种控制器
var result = function(){
//创建控制器实例的时候调用初始化方法
this.init.apply(this, arguments);
}
result.fn = result.prototype;
result.fn.init = function(){}; result.proxy = function(func){
return $.proxy(func, this);
};
result.fn.proxy = result.proxy; result.include = function(ob){
$.extend(this.fn, ob);
};
result.extend = function(ob){
$.extend(this, ob);
};
if(includes){
result.include(includes);
}
return result;
}; exports.Controller = mod;
})(jQuery);

在创建控制器的时候必须手动指定init方法,在init方法为dom元素加入监听:

$(function(){
//创建控制器
var ToggleView = Controller.create({
init: function(view){
this.view = $(view);
this.view.mouseover(this.proxy(this.toggleClass));
this.view.mouseout(this.proxy(this.toggleClass));
},
toggleClass: function(){
this.view.toggleClass("over");
}
}); //创建一个控制器的实例
new ToggleView("#view");
});

在创建实例的时候将在构造函数里触发init事件。另外依据实例化的情况将视图传入控制器而不是写死在控制器内,我们就能够将控制器重用于不同的元素。同一时候保持代码最短。

訪问视图

一种常见的模式是一个视图相应一个控制器,视图包括Id,而在控制器内使用视图的元素则使用class,这样和其它视图的元素不会产生冲突,比方上面的ToggleView传入了Id为view的元素,所以在view内的元素则使用类名进行訪问。

init: function(view){
this.view = $(view);
this.form = this.view.find(".form");
}

但这意味着控制器中会有非常多选择器,须要不断查找DOM,我们能够在控制器中专门开辟一个空间来存放选择器到变量的映射表:

elements: {
"form.searchForm": "searchForm",
"form input[type=text]": "searchInput"
}

有了这种映射表之后。控制器的属性名(比方searchForm)就能和详细的元素(类名为searchForm的form)相相应了,并且在控制器实例化的时候创建他们:

$(function($){
exports.SearchView = Controller.create({
//视图中的元素使用类名查找
elements: {
"input[type=search]": "searchInput",
"form": "searchForm"
}, init:function(element){
//获取视图元素
this.el = $(element);
//依据映射表创建属性
this.refreshElements();
//为元素加入监听等
this.searchForm.submit(this.proxy(this.search));
},
//事件处理函数
search: function(e){
console.log("Searching", this.searchInput.val());
}, //内部使用的选择器,将上下文指定为视图元素
$: function(selector){
return $(selector, this.el);
}, //创建视图内的元素
refreshElements: function(){
for(var key in this.elements){
//key为选择器名。值为属性名
this[this.elements[key]] = this.$(key);
}
}
});
//视图用id指定
new SearchView("#users");
});

状态机

状态机是“有限状态机”的简称,本质上由两部分构成:状态和转换器。它仅仅有一个活动状态,但也包括非常多非活动状态。当活动状态之间相互切换的时候就会调用状态转换器。

比方应用中存在非常多视图,它们的显示是相互独立的,一个视图用来显示联系人,一个视图用来编辑联系人,这两个视图一定是相互排斥关系,当中一个显示还有一个一定隐藏,这个场景就非常适合引入状态机。由于它能确保每一个时刻仅仅有一个是激活的。

首先看一下状态机的思路。我们构造一个状态机:

var StateMachine = function(){};
StateMachine.fn = StateMechine.prototype; StateMachine.fn.bind = function(){
if(!this.o){
this.o = $({});
}
//绑定自己定义事件
this.o.bind.apply(this.o, arguments);
} StateMachine.fn.trigger = function(){
if(!this.o){
this.o = $({});
}
//触发自己定义事件
this.o.trigger.apply(this.o, arguments);
} StateMechine.fn.add = function(controller){
//为状态机绑定自己定义事件
this.bind("change", function(e, current){
if(controller == current){
controller.activate();
}else{
controller.deactivate();
}
}); //为控制器创建激活方法
controller.active = $.proxy(function(){
this.trigger("change", controller);
}, this);
}

(注)jQuery中的自己定义事件

上述代码重点在于bind和trigger,在jQuery中,利用这两个方法能够非常轻易地实现自己定义事件:

var $obj = $({});
$obj.bind("myEvent", function(){
console.log("自己定义事件");
});
$obj.trigger("myEvent"); //"自己定义事件"

bind代表绑定。trigger表示触发,你仅仅须要一个jQuery对象就够了。所以上面创建了一个空的对象,使用$包装成了jQuery对象。

(注解完)

这个状态机的add()方法将传入的控制器加入至状态列表,并为状态机中的o绑定一个自己定义的change事件(加入一个绑定一个)。然后创建一个active()函数。当控制器调用active()的时候。触发所有的change事件,则除了调用的控制器将运行activate方法外。其它控制器所有运行deactivate方法:

//控制器1
var con1 = {
activate: function(){
console.log("con1 activate");
},
deactivate: function(){
console.log("con1 deactivate");
}
};
//控制器2
var con2 = {
activate: function(){
console.log("con2 activate");
},
deactivate: function(){
console.log("con2 deactivate");
}
}; var sm = new StateMachine();
sm.add(con1);
sm.add(con2); con1.active(); //输出"con1 activate"和"con2 deactivate"

当然也能够直接通过状态机触发

sm.trigger("change", con1);

通过状态机的切换状态,我们能够结合控制器改变视图,当con1激活的时候显示一个视图,否则隐藏;con2激活的时候显示还有一个视图,否则隐藏。

这样就能依据状态的不同对视图进行切换。

Javascript MVC 学习笔记(二) 控制器和状态的更多相关文章

  1. JavaScript正则表达式学习笔记(二) - 打怪升级

    本文接上篇,基础部分相对薄弱的同学请移步<JavaScript正则表达式学习笔记(一) - 理论基础>.上文介绍了8种JavaScript正则表达式的属性,本文还会追加介绍几种JavaSc ...

  2. Javascript MVC 学习笔记(一) 模型和数据

    写在前面 近期在看<MVC的Javascript富应用开发>一书.本来是抱着一口气读完的想法去看的.结果才看了一点就傻眼了:太多不懂的地方了. 仅仅好看一点查一点,一点一点往下看吧,进度虽 ...

  3. ThinkPHP 学习笔记 ( 二 ) 控制器 ( Controller )

    /** * ThinkPHP version 3.1.3 * 部署方式:应用部署 * 文内的 http://localhost/ 由实际主机地址代替 */ 入口文件 index.php: <?p ...

  4. Javascript MVC 学习笔记(三) 视图和模板

    模板 Javascript中模板的核心概念是,将包括模板变量的HTML片段和Javascript对象做合并.把模板变量替换为对象中的属性值. 书中讲到了几种库作为模板引擎,可是链接失效了.能够在这里下 ...

  5. JavaScript入门-学习笔记(二)

    关于js变量 变量,就是一个用来存储数据的容器 一般来说,我们的变量都是可以得先声明,再使用,就像是一个东西先必须存在,才能看得见摸得着.然而在js里(es5),可以先使用,后声明. a = 100; ...

  6. spring mvc学习笔记二:@RequestMapping

    @RequestMapping RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上.用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径. @RequestMapp ...

  7. JavaScript Object学习笔记二

    Object.create(proto, [propertiesObject])//创建对象,使用参数一来作为新创建对象的__proto__属性,返回值为在指定原型对象上添加自身属性后的对象 //参数 ...

  8. ASP.NET MVC 学习笔记-7.自定义配置信息 ASP.NET MVC 学习笔记-6.异步控制器 ASP.NET MVC 学习笔记-5.Controller与View的数据传递 ASP.NET MVC 学习笔记-4.ASP.NET MVC中Ajax的应用 ASP.NET MVC 学习笔记-3.面向对象设计原则

    ASP.NET MVC 学习笔记-7.自定义配置信息   ASP.NET程序中的web.config文件中,在appSettings这个配置节中能够保存一些配置,比如, 1 <appSettin ...

  9. .NET MVC 学习笔记(二)— Bootstrap框架

    二..NET MVC 学习笔记(一)—— Bootstrap框架 在实际开发过程中,页面的样式问题是让人很头疼的问题,良好的用户界面可以大大提高用户体检,而在你没有前端开发人员的火力支援情况下,并不是 ...

随机推荐

  1. Distinct Substrings(spoj 694)

    题意:要求不同子串的个数 /* 先求出height数组,不难看出height之和就是重复的字符串个数,用总的减去它就行了. */ #include<cstdio> #include< ...

  2. 为了防止detailsview中修改后,而girdview却没立即更新显示

    原文发布时间为:2008-07-30 -- 来源于本人的百度文章 [由搬家工具导入] 可以在detailsview的事件中添加如下语句,即增加一个头,让它在0秒的时候刷新: Response.AddH ...

  3. h5页面判断微信端用浏览器打开代码

    <div class="weixin-tip"> <p> <img src="img/live_weixin.png" alt=& ...

  4. polyfill for Function--源码

    /** * polyfill for Function */ // from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refer ...

  5. 标准C程序设计七---102

    Linux应用             编程深入            语言编程 标准C程序设计七---经典C11程序设计    以下内容为阅读:    <标准C程序设计>(第7版) 作者 ...

  6. hdu 5690(模运算)

    All X Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submi ...

  7. 从 React Router 谈谈路由的那些事

    React Router 是专为 React 设计的路由解决方案,在使用 React 来开发 SPA (单页应用)项目时,都会需要路由功能,而 React Router 应该是目前使用率最高的. Re ...

  8. Swoole RPC 的实现

    目录 概述 实现效果 代码 小结 概述 这是关于 Swoole 学习的第七篇文章:Swoole RPC 的实现. 第六篇:Swoole 整合成一个小框架 第五篇:Swoole 多协议 多端口 的应用 ...

  9. CMDB与自动化运维,一切尽在掌握中?

    生产力跟不上生产的速度时,就会出现很多问题,如何针对问题进行处理,制定什么样的计划,如何解决就是需要思考的难点? T运维的分类 IT运维,指的是对已经搭建好的网络,软件,硬件进行维护.运维领域也是细分 ...

  10. 域名缓存侦测(DNS Cache Snooping)技术

    域名缓存侦测(DNS Cache Snooping)技术 在企业网络中,通常都会配置DNS服务器,为网络内的主机提供域名解析服务.这些DNS不仅解析自己的私有域名,还会用递归方式,请求公网的DNS解析 ...