本來想寫一篇關(guān)于前端數(shù)據(jù)驅(qū)動的文章,百度搜了一下關(guān)鍵詞浮声,發(fā)現(xiàn)有人已經(jīng)寫了虚婿,文章內(nèi)容和我想寫的差不多,順手就轉(zhuǎn)過來了泳挥。
前段時間一直在想前端MVC的意義然痊。這個話題仁者見仁,但是MVC的使用方法給我提了一個管理數(shù)據(jù)的有意思的想法--數(shù)據(jù)管理和數(shù)據(jù)驅(qū)動頁面羡洁。我們以前的思路一直是事件驅(qū)動頁面玷过,事件驅(qū)動頁面合乎邏輯而且節(jié)約代碼。但是往往代碼組織結(jié)構(gòu)非常松散,這個松散并不是大家所期望的松耦合辛蚊,而是一種亂七八糟的感覺粤蝎,后來在一次code中,我嘗試了一下用數(shù)據(jù)來驅(qū)動頁面袋马,覺得效果也不錯初澎,邏輯也比較簡單。下面簡單分享一下我的思路虑凛。
我有一個電子商店碑宴,我需要一個購物車功能。
我希望購物車能在前端處理相關(guān)邏輯桑谍。而后臺只是保存用戶訂單延柠。
下面是訂單保存的數(shù)據(jù)格式:
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
}
首先我們使用一個數(shù)據(jù)管理器來維護(hù)用戶的訂單數(shù)據(jù),我們把它設(shè)計為一個單體模式锣披。
var shppingCar = function() {
var orderList = {}
this.add = function(obj){
//添加一條購買數(shù)據(jù)
}
this.remove = function(obj){
//刪除一條購買數(shù)據(jù)
}
this.getTotilPrice = function(obj){
//獲取總價
}
this.update = function(obj){
//更新購買數(shù)量
}
this.getOrder = function(){
return orderList;
}
}
這看起來數(shù)據(jù)結(jié)構(gòu)清晰贞间,代碼組織似乎也不錯。接下來涉及到我們DOM部分的操作了雹仿。
var order = new shppingCar();
orderList = order.getOrder();
var htmlManager = function(list){
//用orderList數(shù)據(jù)渲染頁面增热。
}
//第一次初始化數(shù)據(jù)
htmlManager();
//添加一條數(shù)據(jù)
orderList.add({});
orderList = order.getOrder();
htmlManager(orderList);
//刪除一條數(shù)據(jù)
orderList.add(id);
orderList = order.getOrder();
htmlManager(orderList);
//更新一條數(shù)據(jù)
orderList.update(id);
orderList = order.getOrder();
htmlManager(orderList);
每做一次數(shù)據(jù)操作,我們都要更新一次數(shù)據(jù)胧辽。我們沒有辦法改變這個事實峻仇,因為事實就是數(shù)據(jù)改變,我們必然要修改頁面邑商。
或許你有更好的辦法摄咆,那就是不用orderList渲染DOM,而是用一個回調(diào)函數(shù)來處理奠骄。那么代碼變?yōu)?/p>
this.add = function(obj,fn){
//添加一條購買數(shù)據(jù)
if(fn){
fn();
}
}
你可以這樣使用
orderList.add({},function(){
//解析一次數(shù)據(jù)豆同,生成一條DOM結(jié)構(gòu),插入
//更改總價
});
這樣也意味著你分別要為刪除含鳞、添加影锈、更新書寫不同的回調(diào)函數(shù),看起來也并不是一個非常好的辦法蝉绷。
回到前面的代碼鸭廷,我們只需要做一個小小的改變,就可以用數(shù)據(jù)的改變來驅(qū)動我們的頁面更新熔吗,這也是一個偽觀察者模式辆床。其思想就是:數(shù)據(jù)更新了,我要重新渲染頁面桅狠。
var shppingCar = function() {
var orderList = {}
//我們給shppingCar添加了一個私有方法讼载,當(dāng)數(shù)據(jù)改變時自動為我們來更新頁面轿秧。
var render= function(){
}
this.add = function(obj){
//添加一條購買數(shù)據(jù)
render();
}
this.remove = function(obj){
//刪除一條購買數(shù)據(jù)
render();
}
this.getTotilPrice = function(obj){
//獲取總價
render();
}
this.update = function(obj){
//更新購買數(shù)量
render();
}
this.getOrder = function(){
return orderList;
}
}
這樣我們使用的時候,就可以這樣了
var orderList = new shppingCar();
//添加一條數(shù)據(jù)
orderList.add({});
我們只是把外部渲染函數(shù)改成了購物車對象的私有方法咨堤,然后在數(shù)據(jù)變動時調(diào)用這個私有方法菇篡,就可以省去了在外部每次更新數(shù)據(jù)都要再次調(diào)用一個更新頁面的方法。雖然代碼量減少的不是很多一喘,但是將所有的內(nèi)容封裝起來外面調(diào)用看起來更是省心省力驱还。
至于刪除數(shù)據(jù)和更新數(shù)據(jù),我們甚至不需要在外部定義凸克,直接在渲染頁面的時候把事件綁定到元素之后即可(下面的示例代碼我實現(xiàn)了一個刪除綁定议蟆,修改商品個數(shù)的功能大家有興趣可以自己實現(xiàn)。)
var shppingCar = function() {
//我們把數(shù)據(jù)設(shè)計為這樣的格式
var orderList = {
length:0,
subscriberId:'254',
totalPrice:0
}
//一些工具方法
//通過圖書id獲取當(dāng)前是第幾條數(shù)據(jù)
var getItemById = function(id){
for (var i = 0; i < orderList.length; i++) {
if(orderList[i].id == id) {
return i;
}
}
}
//重新整理數(shù)據(jù)成為標(biāo)準(zhǔn)格式
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>訂購數(shù)量:"+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)
}
//綁定修改個數(shù)事件
};
//刪除一條數(shù)據(jù)
var remove = function(id){
var item = getItemById(id);
delete orderList[item];
orderList.length-=1;
refreshData();
orderList.totalPrice = updateTotilPrice();
htmlManager();
}
//更新商品個數(shù)
var update = function(id,amount){
//TODO:更新購買數(shù)量
orderList.totalPrice = updateTotilPrice();
htmlManager();
}
//對外倆個接口方法萎战,一個可以添加一條購買數(shù)據(jù)咐容,一個為獲取當(dāng)前購物車的所有數(shù)據(jù)
this.add = function(obj){
//TODO:驗證傳入的數(shù)據(jù)是否合法
//TODO:此處判斷是否已經(jīng)存在該商品,如果存在蚂维,則調(diào)用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());
};
轉(zhuǎn)載自javascript 中的數(shù)據(jù)驅(qū)動頁面模式
另外推薦一篇文章js面向數(shù)據(jù)編程(DOP)