javascript的MVC三層架構(案例之分頁插件)
作者:田想兵 博客地址:http://www.cnblogs.com/tianxiangbing
最近很少寫博文,一是比較忙,二是沒啥心情,好,言歸正傳,今天的主題是MVC版的javascript結構,做程序的,可能對MVC會有較深刻的印象,就是Model-View-Control,中文的意思就是模型-視圖-控制器,這好像已經是一個很成熟的結構了,后來又有些在它基礎上的拓展,有興趣的可以擺渡一下。所以它基本上適應任何情況下的編程,今天我們就要用它來實現js版的一個分頁控件。
首先,我們先明確每一層是做什么的:
View也就是視圖層,在這里面我們會去初始化一些html元素;
Model模型層,我一直認為這一層存在的意義不大,因為通常我們new一個js對象的時候,都會初始化它的一些變量,很少會去單獨寫個方法去設置它,所以就把Model改成發送ajax請求了;
Control控制器,這里就是一些業務邏輯了,這里我們可以再分出一個事件層來;
Event事件層,處理html事件。
中途停了一天,接着寫,不知道為啥,最近一直都沒有辦法集中精力,可能是因為兒童節吧,今天還要加班, 可悲的碼農啊,題外話不說了,繼續碼字。
現在我們來分析下需求,分頁控件,無非就是對數據的一個分組顯示,所以它一定會有pagesize(每頁條數)和count(總條數) 這兩個屬性,當然也有人喜歡把所有數據返回過來給前端來分頁,不過分頁的目的之一,就是為了減輕數據量,一次批量返回也不是不行,具體情況具體分析吧!有了count和pagesize后,我們就可以算出總頁碼數了。
這個很簡單,就是整除有余的話就多一頁,否則取整數部分。
我們先看下效果圖,不然的話,腦子里沒有一個結構,也是無法下手的。請觀看下圖:

接着該MVC三層結構出場了,我們先在view(視圖層)初始化一些必要的HTML元素:
var _self= this;
var _class={
page: function(args){
var _html='\
<div class="pager">\
<a class="firstPage" href="javascript:void(0);">首頁</a>\
<a class="prePage" href="javascript:void(0);">上一頁</a>\
<span class="inputPage">第<input type="text" value="'+ _self.currentIndex +'" class="txt_curIndex" name="txt_curIndex"/>頁/<i>'+_self.sumPage+'</i>頁</span>\
<a class="nextPage" href="javascript:void(0);">下一頁</a>\
<a class="lastPage" href="javascript:void(0);">末頁</a>\
</div>\
';
_self.content.html(_html);
_self.event("bind",args);
}
};
return _class[method](args);
}
在這里,我又調用了事件層,來給這些HTML元素綁定相應的事件,這里大概有五個事件,就是上一頁,下一頁,首頁,未頁,及跳轉。這些事件,其本質就是改變頁碼數,好,那我們就寫個請求頁碼數的方法:
var _self= this;
var _class={
go: function(args){
return $.ajax({
url:_self.ajaxUrl,
dataType:"json",
async: true,
data:args[0],
success: function(data){
args[1](data);
_self.cpu("change",data);
},
type:"GET",
error: function(data){
alert("json格式不正確")
}
});
}
};
return _class[method](args);
}
這是個ajax請求,放在model層下面,有兩個參數,一個是ajax需要傳的參數{page:1} args[0],另一個是外部的一個回調方法,用來格式化內容的,這個跟分頁控件沒有半毛錢關系,所以就當作回調。好,接着我們在控制器層里就調用這個方法就行了,剛才說到有五個事件,這樣就對應了五個控制器:

var _self= this;
var _class={
change: function(data){
var arr = data;
if (data.count>0){
_self.count=data.count;
_self.sumPage= parseInt( _self.count % _self.pageSize >0 ? _self.count / _self.pageSize+1 : _self.count / _self.pageSize);
_self.view("page");
}
},
jump: function(args){
var input = parseInt($("[name='txt_curIndex']",_self.content).val());
if (input>0 && input <= _self.sumPage){
_self.currentIndex = input;
_self.ajaxArgs.page = _self.currentIndex-1;
_self.model("go",[_self.ajaxArgs,_self.returnFunc]);
}
},
prev: function(args){
if (_self.currentIndex > 1){
_self.currentIndex--;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
},
next: function(args){
if (_self.currentIndex < _self.sumPage - 1){
_self.currentIndex++;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
},
last: function(args){
_self.currentIndex = _self.sumPage;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
},
first: function(args){
_self.currentIndex = 1;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
};
return _class[method](args);
}
};
數一數,是不是五個,我勒個去,作者數學不好,竟然是他妹的六個,竟然多了個change,好啦,把chage這個放model里去吧,他的作用就是計算一些變量的值。
最后事件層只需要調用Control控制層相對應的方法就行了:
var _self= this;
var pager = $("div.pager",_self.content);
var _class={
bind: function(args){
$("[name='txt_curIndex']",_self.content).keydown( function(e){
if (e.keyCode==13){
_self.cpu("jump",args);
}
});
$(".prePage",_self.content).click( function(){
_self.cpu("prev",args);
});
$(".nextPage",_self.content).click( function(){
_self.cpu("next",args);
});
$(".lastPage",_self.content).click( function(){
_self.cpu("last",args);
});
$(".firstPage",_self.content).click( function(){
_self.cpu("first",args);
});
}
};
return _class[method](args);
},
這樣是不是就完了呢,你猜?沒錯,恭喜你,猜對了,還沒有結束,因為我們還沒有看到入口,一般情況我們都喜歡定義一個名為init的方法來初始化,這次的情況也很一般,所以定義init吧:
var _self = this;
_self.currentIndex = ops.currentIndex;
_self.ajaxArgs = $.extend( ops.ajaxArgs,{page: this.currentIndex-1});
_self.model("go",[_self.ajaxArgs,_self.returnFunc]);
},
整個分頁插件的代碼如下:

this.currentIndex = 1;
this.count = ops.count;
this.pageSize = ops.pageSize||10;
this.sumPage = 1;
this.content = ops.content;
this.ajaxUrl = ops.url;
this.returnFunc=ops.returnFunc|| new Function();
};
Pager.prototype={
init: function(ops){
var _self = this;
_self.currentIndex = ops.currentIndex;
_self.ajaxArgs = $.extend( ops.ajaxArgs,{page: this.currentIndex-1});
_self.model("go",[_self.ajaxArgs,_self.returnFunc]);
},
view: function(method,args){
var _self= this;
var _class={
page: function(args){
var _html='\
<div class="pager">\
<a class="firstPage" href="javascript:void(0);">首頁</a>\
<a class="prePage" href="javascript:void(0);">上一頁</a>\
<span class="inputPage">第<input type="text" value="'+ _self.currentIndex +'" class="txt_curIndex" name="txt_curIndex"/>頁/<i>'+_self.sumPage+'</i>頁</span>\
<a class="nextPage" href="javascript:void(0);">下一頁</a>\
<a class="lastPage" href="javascript:void(0);">末頁</a>\
</div>\
';
_self.content.html(_html);
_self.event("bind",args);
}
};
return _class[method](args);
},
event: function(method,args){
var _self= this;
var pager = $("div.pager",_self.content);
var _class={
bind: function(args){
$("[name='txt_curIndex']",_self.content).keydown( function(e){
if (e.keyCode==13){
_self.cpu("jump",args);
}
});
$(".prePage",_self.content).click( function(){
_self.cpu("prev",args);
});
$(".nextPage",_self.content).click( function(){
_self.cpu("next",args);
});
$(".lastPage",_self.content).click( function(){
_self.cpu("last",args);
});
$(".firstPage",_self.content).click( function(){
_self.cpu("first",args);
});
}
};
return _class[method](args);
},
model: function(method,args){
var _self= this;
var _class={
go: function(args){
return $.ajax({
url:_self.ajaxUrl,
dataType:"json",
async: true,
data:args[0],
success: function(data){
args[1](data);
_self.model("change",data);
},
type:"GET",
error: function(data){
console.dir(data)
alert("json格式不正確")
}
});
},
change: function(data){
var arr = data;
if (data.count>0){
_self.count=data.count;
_self.sumPage= parseInt( _self.count % _self.pageSize >0 ? _self.count / _self.pageSize+1 : _self.count / _self.pageSize);
_self.view("page");
}
}
};
return _class[method](args);
},
cpu: function(method,args){
var _self= this;
var _class={
jump: function(args){
var input = parseInt($("[name='txt_curIndex']",_self.content).val());
if (input>0 && input <= _self.sumPage){
_self.currentIndex = input;
_self.ajaxArgs.page = _self.currentIndex-1;
_self.model("go",[_self.ajaxArgs,_self.returnFunc]);
}
},
prev: function(args){
if (_self.currentIndex > 1){
_self.currentIndex--;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
},
next: function(args){
if (_self.currentIndex < _self.sumPage - 1){
_self.currentIndex++;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
},
last: function(args){
_self.currentIndex = _self.sumPage;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
},
first: function(args){
_self.currentIndex = 1;
$("[name='txt_curIndex']",_self.content).val(_self.currentIndex);
_self.cpu("jump",args);
}
};
return _class[method](args);
}
};
OK,最后我們回過頭來看下,這個結構的優劣點,優點了,就是更方便於擴展,可以無限制的往下加,層次分得較清明,劣點是,層次過深,效率會低一些,而且看着不爽,所以我給他定位為,管理系統業務邏輯較多時使用,一般的JS效果插件還是使用扁平結構的好。今天就寫到這吧,謝謝您的觀看,最后的台詞是:如果你有任何的疑問都不要來問我,請反復閱讀本文。也可以加入我的QQ群與其他人討論,本文的DEMO會放在群共享里。我的群號有5678537,70210212,閉幕.