前段時間一直在想前端MVC的意義。這個話題仁者見仁,但是MVC的使用方法給我提了一個管理數據的有意思的想法--數據管理和數據驅動頁面。我們以前的思路一直是事件驅動頁面,事件驅動頁面合乎邏輯而且節約代碼。但是往往代碼組織結構非常松散,這個松散並不是大家所期望的松耦合,而是一種亂七八糟的感覺,后來在一次code中,我嘗試了一下用數據來驅動頁面,覺得效果也不錯,邏輯也比較簡單。下面簡單分享一下我的思路。
- 我有一個電子商店,我需要一個購物車功能。
- 我希望購物車能在前端處理相關邏輯。而后台只是保存用戶訂單。
下面是訂單保存的數據格式。
var orderList = {
0:{
'id':'12653',
'productName':'Kindle fire',
'price':790,
'amount':2,
'discount':0.75
},
1:{
'id':'2653',
'productName':'iPad',
'price':2790,
'amount':10,
'discount':0.70
},
2:{
'id':'653',
'productName':'Mac',
'price':7900,
'amount':1,
'discount':0.95
},
length:3,
subscriberId:'254',
totalPrice:0
}
首先我們使用一個數據管理器來維護用戶的訂單數據,我們把它設計為一個單體模式。
var shppingCar = function() {
var orderList = {}
this.add = function(obj){
//添加一條購買數據
}
this.remove = function(obj){
//刪除一條購買數據
}
this.getTotilPrice = function(obj){
//獲取總價
}
this.update = function(obj){
//更新購買數量
}
this.getOrder = function(){
return orderList;
}
}
這看起來數據結構清晰,代碼組織似乎也不錯。接下來涉及到我們DOM部分的操作了。
var order = new shppingCar();
orderList = order.getOrder();
var htmlManager = function(list){
//用orderList數據渲染頁面。
}
//第一次初始化數據
htmlManager();
//添加一條數據
orderList.add({});
orderList = order.getOrder();
htmlManager(orderList);
//刪除一條數據
orderList.add(id);
orderList = order.getOrder();
htmlManager(orderList);
//更新一條數據
orderList.update(id);
orderList = order.getOrder();
htmlManager(orderList);
每做一次數據操作,我們都要更新一次數據。我們沒有辦法改變這個事實,因為事實就是數據改變,我們必然要修改頁面。
或許你有更好的辦法,那就是不用orderList渲染DOM,而是用一個回調函數來處理。那么代碼變為
this.add = function(obj,fn){
//添加一條購買數據
if(fn){
fn();
}
}
你可以這樣使用
orderList.add({},function(){
//解析一次數據,生成一條DOM結構,插入
//更改總價
});
這樣也意味着你分別要為刪除、添加、更新書寫不同的回調函數,看起來也並不是一個非常好的辦法。
回到前面的代碼,我們只需要做一個小小的改變,就可以用數據的改變來驅動我們的頁面更新,這也是一個偽觀察者模式。其思想就是:數據更新了,我要重新渲染頁面。
var shppingCar = function() {
var orderList = {}
//我們給shppingCar添加了一個私有方法,當數據改變時自動為我們來更新頁面。
var rander = function(){
}
this.add = function(obj){
//添加一條購買數據
rander();
}
this.remove = function(obj){
//刪除一條購買數據
rander();
}
this.getTotilPrice = function(obj){
//獲取總價
rander();
}
this.update = function(obj){
//更新購買數量
rander();
}
this.getOrder = function(){
return orderList;
}
}
這樣我們使用的時候,就可以這樣了
var orderList = new shppingCar();
//添加一條數據
orderList.add({});
我們只是把外部渲染函數改成了購物車對象的私有方法,然后在數據變動時調用這個私有方法,就可以省去了在外部每次更新數據都要再次調用一個更新頁面的方法。雖然代碼量減少的不是很多,但是將所有的內容封裝起來外面調用看起來更是省心省力。
至於刪除數據和更新數據,我們甚至不需要在外部定義,直接在渲染頁面的時候把事件綁定到元素之后即可(下面的示例代碼我實現了一個刪除綁定,修改商品個數的功能大家有興趣可以自己實現。)
我不知道這樣做是否有意義,希望大家拍。
附測試代碼如下。
var shppingCar = function() {
//我們把數據設計為這樣的格式
var orderList = {
length:0,
subscriberId:'254',
totalPrice:0
}
//一些工具方法
//通過圖書id獲取當前是第幾條數據
var getItemById = function(id){
for (var i = 0; i < orderList.length; i++) {
if(orderList[i].id == id) {
return i;
}
}
}
//重新整理數據成為標准格式
var refreshData = function(){
var o = {},n = [];
for (var key in orderList) {
var k = Number(key);
if(!isNaN(k)){
n.push(orderList[key]);
}else{
o[key] = orderList[key];
}
}
for (var i = 0; i < n.length; i++) {
o[i] = n[i];
}
orderList = o;
}
//計算總價
var updateTotilPrice = function() {
var totalprice = 0;
for (var i = 0; i < orderList.length; i++) {
totalprice +=orderList[i].price*orderList[i].discount*orderList[i].amount;
}
return totalprice;
};
//渲染頁面
var htmlManager = function () {
var items = "<ul>";
for (var i=0;i<orderList.length;i++) {
items += "<li><span>商品編號:"+orderList[i].id
+"</span> <span>商品名字:"+orderList[i].productName
+"</span> <span>商品價格:"+orderList[i].price
+"</span> <span>訂購數量:"+orderList[i].amount
+"</span> <span>商品折扣:"+orderList[i].discount+"</span>"
+"<a data-id="+orderList[i].id+" href='###'>刪除</a></li>"
}
items += "</ul>";
items+="商品總價格為"+ orderList.totalPrice +"元";
document.getElementsByTagName("body")[0].innerHTML = (items);
//綁定刪除事件
var delBtns = document.getElementsByTagName("a");
for (var j = 0; j < delBtns.length; j++) {
(function(k){
delBtns[k].onclick = function(){
remove(delBtns[k].getAttribute('data-id'));
return false;
}
})(j)
}
//綁定修改個數事件
};
//刪除一條數據
var remove = function(id){
var item = getItemById(id);
delete orderList[item];
orderList.length-=1;
refreshData();
orderList.totalPrice = updateTotilPrice();
htmlManager();
}
//更新商品個數
var update = function(id,amount){
//TODO:更新購買數量
orderList.totalPrice = updateTotilPrice();
htmlManager();
}
//對外倆個接口方法,一個可以添加一條購買數據,一個為獲取當前購物車的所有數據
this.add = function(obj){
//TODO:驗證傳入的數據是否合法
//TODO:此處判斷是否已經存在該商品,如果存在,則調用updata方法。
orderList[orderList.length] = obj;
if(orderList[orderList.length]){
orderList.length +=1;
}
orderList.totalPrice = updateTotilPrice();
htmlManager();
}
this.getOrder = function() {
return orderList;
};
};
//使用方法:
var orderList = new shppingCar();
orderList.add({
'id':'6530',
'productName':'Mac mini-0',
'price':4900,
'amount':4,
'discount':0.90
})
orderList.add({
'id':'65301',
'productName':'Mac mini-1',
'price':5000,
'amount':4,
'discount':0.90
})
document.onclick = function() {
console.log(orderList.getOrder());
};
