javascript 中的數(shù)據(jù)驅(qū)動(dòng)頁(yè)面模式

本來(lái)想寫(xiě)一篇關(guān)于前端數(shù)據(jù)驅(qū)動(dòng)的文章,百度搜了一下關(guān)鍵詞,發(fā)現(xiàn)有人已經(jīng)寫(xiě)了,文章內(nèi)容和我想寫(xiě)的差不多,順手就轉(zhuǎn)過(guò)來(lái)了。
前段時(shí)間一直在想前端MVC的意義。這個(gè)話題仁者見(jiàn)仁,但是MVC的使用方法給我提了一個(gè)管理數(shù)據(jù)的有意思的想法--數(shù)據(jù)管理和數(shù)據(jù)驅(qū)動(dòng)頁(yè)面。我們以前的思路一直是事件驅(qū)動(dòng)頁(yè)面,事件驅(qū)動(dòng)頁(yè)面合乎邏輯而且節(jié)約代碼。但是往往代碼組織結(jié)構(gòu)非常松散,這個(gè)松散并不是大家所期望的松耦合,而是一種亂七八糟的感覺(jué),后來(lái)在一次code中,我嘗試了一下用數(shù)據(jù)來(lái)驅(qū)動(dòng)頁(yè)面,覺(jué)得效果也不錯(cuò),邏輯也比較簡(jiǎn)單。下面簡(jiǎn)單分享一下我的思路。

我有一個(gè)電子商店,我需要一個(gè)購(gòu)物車功能。
我希望購(gòu)物車能在前端處理相關(guān)邏輯。而后臺(tái)只是保存用戶訂單。

下面是訂單保存的數(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
}

首先我們使用一個(gè)數(shù)據(jù)管理器來(lái)維護(hù)用戶的訂單數(shù)據(jù),我們把它設(shè)計(jì)為一個(gè)單體模式。

var shppingCar = function() {
    var orderList = {}
    this.add = function(obj){
        //添加一條購(gòu)買數(shù)據(jù)
    }
    this.remove = function(obj){
        //刪除一條購(gòu)買數(shù)據(jù)
    }
    this.getTotilPrice = function(obj){
        //獲取總價(jià)
    }
    this.update = function(obj){
        //更新購(gòu)買數(shù)量
    }
    this.getOrder = function(){
        return orderList;
    }
}

這看起來(lái)數(shù)據(jù)結(jié)構(gòu)清晰,代碼組織似乎也不錯(cuò)。接下來(lái)涉及到我們DOM部分的操作了。

var order = new shppingCar();
orderList = order.getOrder();
var htmlManager = function(list){
    //用orderList數(shù)據(jù)渲染頁(yè)面。
}
//第一次初始化數(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ù)。我們沒(méi)有辦法改變這個(gè)事實(shí),因?yàn)槭聦?shí)就是數(shù)據(jù)改變,我們必然要修改頁(yè)面。
或許你有更好的辦法,那就是不用orderList渲染DOM,而是用一個(gè)回調(diào)函數(shù)來(lái)處理。那么代碼變?yōu)?/p>

this.add = function(obj,fn){
    //添加一條購(gòu)買數(shù)據(jù)
    if(fn){
        fn();
    }
}
你可以這樣使用
orderList.add({},function(){
    //解析一次數(shù)據(jù),生成一條DOM結(jié)構(gòu),插入
    //更改總價(jià)
});

這樣也意味著你分別要為刪除、添加、更新書(shū)寫(xiě)不同的回調(diào)函數(shù),看起來(lái)也并不是一個(gè)非常好的辦法。

回到前面的代碼,我們只需要做一個(gè)小小的改變,就可以用數(shù)據(jù)的改變來(lái)驅(qū)動(dòng)我們的頁(yè)面更新,這也是一個(gè)偽觀察者模式。其思想就是:數(shù)據(jù)更新了,我要重新渲染頁(yè)面。

var shppingCar = function() {
    var orderList = {}
 
    //我們給shppingCar添加了一個(gè)私有方法,當(dāng)數(shù)據(jù)改變時(shí)自動(dòng)為我們來(lái)更新頁(yè)面。
    var render= function(){
 
    }
    this.add = function(obj){
        //添加一條購(gòu)買數(shù)據(jù)
        render();
    }
    this.remove = function(obj){
        //刪除一條購(gòu)買數(shù)據(jù)
        render();
    }
    this.getTotilPrice = function(obj){
        //獲取總價(jià)
        render();
    }
    this.update = function(obj){
        //更新購(gòu)買數(shù)量
        render();
    }
    this.getOrder = function(){
        return orderList;
    }
}

這樣我們使用的時(shí)候,就可以這樣了

var orderList = new shppingCar();
//添加一條數(shù)據(jù)
orderList.add({});

我們只是把外部渲染函數(shù)改成了購(gòu)物車對(duì)象的私有方法,然后在數(shù)據(jù)變動(dòng)時(shí)調(diào)用這個(gè)私有方法,就可以省去了在外部每次更新數(shù)據(jù)都要再次調(diào)用一個(gè)更新頁(yè)面的方法。雖然代碼量減少的不是很多,但是將所有的內(nèi)容封裝起來(lái)外面調(diào)用看起來(lái)更是省心省力。

至于刪除數(shù)據(jù)和更新數(shù)據(jù),我們甚至不需要在外部定義,直接在渲染頁(yè)面的時(shí)候把事件綁定到元素之后即可(下面的示例代碼我實(shí)現(xiàn)了一個(gè)刪除綁定,修改商品個(gè)數(shù)的功能大家有興趣可以自己實(shí)現(xiàn)。)

var shppingCar = function() {
    //我們把數(shù)據(jù)設(shè)計(jì)為這樣的格式
    var orderList = {
        length:0,
        subscriberId:'254',
        totalPrice:0
    }
    //一些工具方法
    //通過(guò)圖書(shū)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;
    }
    //計(jì)算總價(jià)
    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;
    };
    //渲染頁(yè)面
    var htmlManager = function () {
        var items = "<ul>";
        for (var i=0;i<orderList.length;i++) {
            items += "<li><span>商品編號(hào):"+orderList[i].id
                    +"</span> <span>商品名字:"+orderList[i].productName
                    +"</span> <span>商品價(jià)格:"+orderList[i].price
                    +"</span> <span>訂購(gòu)數(shù)量:"+orderList[i].amount
                    +"</span> <span>商品折扣:"+orderList[i].discount+"</span>"
                    +"<a data-id="+orderList[i].id+" href='###'>刪除</a></li>"
        }
        items += "</ul>";
        items+="商品總價(jià)格為"+ 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)
        }
        //綁定修改個(gè)數(shù)事件
    };
    //刪除一條數(shù)據(jù)
    var remove = function(id){
        var item = getItemById(id);
        delete orderList[item];
        orderList.length-=1;
        refreshData();
        orderList.totalPrice = updateTotilPrice();
        htmlManager();
    }
    //更新商品個(gè)數(shù)
    var update = function(id,amount){
        //TODO:更新購(gòu)買數(shù)量
        orderList.totalPrice = updateTotilPrice();
        htmlManager();
    }
    //對(duì)外倆個(gè)接口方法,一個(gè)可以添加一條購(gòu)買數(shù)據(jù),一個(gè)為獲取當(dāng)前購(gòu)物車的所有數(shù)據(jù)
    this.add = function(obj){
        //TODO:驗(yàn)證傳入的數(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ū)動(dòng)頁(yè)面模式

另外推薦一篇文章js面向數(shù)據(jù)編程(DOP)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容