javascript 中的數(shù)據(jù)驅(qū)動頁面模式

本來想寫一篇關(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)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市鸟雏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌览祖,老刑警劉巖孝鹊,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異展蒂,居然都是意外死亡又活,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門锰悼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柳骄,“玉大人,你說我怎么就攤上這事箕般∧褪恚” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵丝里,是天一觀的道長曲初。 經(jīng)常有香客問我,道長杯聚,這世上最難降的妖魔是什么臼婆? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮幌绍,結(jié)果婚禮上颁褂,老公的妹妹穿的比我還像新娘故响。我一直安慰自己,他們只是感情好颁独,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布彩届。 她就那樣靜靜地躺著,像睡著了一般奖唯。 火紅的嫁衣襯著肌膚如雪惨缆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天丰捷,我揣著相機與錄音坯墨,去河邊找鬼。 笑死病往,一個胖子當(dāng)著我的面吹牛捣染,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播停巷,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼耍攘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了畔勤?” 一聲冷哼從身側(cè)響起蕾各,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎庆揪,沒想到半個月后式曲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡缸榛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年吝羞,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片内颗。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡钧排,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出均澳,到底是詐尸還是另有隱情恨溜,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布找前,位于F島的核電站筒捺,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏纸厉。R本人自食惡果不足惜系吭,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颗品。 院中可真熱鬧肯尺,春花似錦沃缘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至氓仲,卻和暖如春水慨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背敬扛。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工晰洒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人啥箭。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓谍珊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親急侥。 傳聞我的和親對象是個殘疾皇子砌滞,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容