最近分別使用 Zepto 和 Avalon框架寫了個 SPA項目,貼出來討論下 JS DOM操做爲主 JS庫 與 MV* 框架的對比javascript
以 DOM操做 JS庫實現:jQuery、Zepto、MooTools)css
經過後端控制器渲染頁面html
view:前端
<{css src="page/cart.css"}> <header class="titleHead"> <div class="leftBtns"> <a class="leftBack goBack" href="javascript:void(0)">←</a> </div> <b>購物車</b> </header> <div id="emptyBox" <{if !empty($cart.cartList)}> style='display: none;'<{/if}> > <p>購物車空空如也</p> <p><a href="<{link app='wechatecoupon' ctl='site_cashcoupon' act='group'}>">去添加</a></p> </div> <{if !empty($cart.cartList)}> <div class="box"> <div id="topTotal"> <span>商品總價(不含運費) <b class="total"><{$cart.totalAmount|cur}></b></span> <a href="<{link app='wechatecoupon' ctl='site_order' act='create'}>" class="topBtn">去結算</a> </div> <div class="goodslistWrap"> <{foreach from=$cart.cartList item=item}> <div class="goodslist" data-cart-id="<{$item.cart_id}>" data-cashcoupon-id="<{$item.obj_id}>" data-price="<{$item.cashcoupon.price}>"> <div class="imgWrap"> <img src="<{$item.cashcoupon.image_id|storager}>" height="100%"/> </div> <div class="txtWrap"> <p><{$item.cashcoupon.name}></p> <div class="handleBox"> <div class="numBox"> <span class="handle minus <{if 1==$item.quantity}>bg_gray<{/if}>">-</span> <span> <input type="text" name="num" value="<{$item.quantity}>" data-max-count="<{$item.cashcoupon.sale_count}>" maxlength="4" readonly="readonly" /> </span> <span class="handle plus <{if $item.quantity >= $item.cashcoupon.sale_count}>bg_gray<{/if}>">+</span> </div> <div class="trashBox del"> <i class="trash"></i> </div> </div> </div> <div class="priceWrap"> <span><strong><{$item.cashcoupon.price|cur}></strong></span> <span><del><{$item.cashcoupon.mktprice|cur}></del></span> </div> </div> <{/foreach}> <div class="clear"></div> </div> <div class="goodslistTotal"> <span>商品總價(不含運費)<b class="total"><{$cart.totalAmount|cur}></b></span> </div> <div class="bottomTotal"> <div class="delAll"> <i class="trash_b"></i> <span>清空所有</span> </div> <div class="payBtnBox"> <a href="<{link app='wechatecoupon' ctl='site_order' act='create'}>" class="bottomBtn">去結算</a> </div> </div> </div> <{script src="page/cart.js"}> <{/if}>
JS 邏輯java
javascript
// 全局常量 var UA = navigator.userAgent; var ipad = !!(UA.match(/(iPad).*OS\s([\d_]+)/)), isIphone = !!(!ipad && UA.match(/(iPhone\sOS)\s([\d_]+)/)), isAndroid = !!(UA.match(/(Android)\s+([\d.]+)/)), isMobile = !!(isIphone || isAndroid); var CLICK = isMobile ? "tap" : 'click'; // 移動端觸摸、PC單擊 事件 // 購物車 var Cart = { // 更新 info update: function ($id, $number, fun) { var data = { id: $id || '', number: $number || 0 }; CGI.POST('wecart-update.html', data, function (data) { // 回調方法 fun && fun(data); }); }, // 計算總價 figurePrice: function () { var goodsList = $(".goodslist"); console.log(goodsList); if (0 === goodsList.length) { this.removeAll(); return false; } // 當前商品金額 var price; // 當前商品數量 var number; // 總金額 var allPrice = 0; $.each(goodsList, function (index, item) { item = $(item); price = parseFloat(item.attr("data-price")); number = parseInt(item.find("input").val()); console.log({'數量': number, '單價': price}); allPrice += price * number; }); console.log('總價:' + allPrice); // DOM 操做 $(".total").text("¥" + allPrice.toFixed(2)); }, // 移除全部 removeAll: function () { // DOM 操做 $("#emptyBox").show(); $(".box").hide(); } }; // 遞增、遞減 $(".numBox").on(CLICK, function (e) { // numBox var _t = $(this); var dom = $(e.target); // 商品數量 DOM var numDom = _t.find("input"); //console.log(numDom); // 數量 var _v = parseInt(numDom.val()), now_v; // 最大購買數 var max = parseInt(numDom.attr("data-max-count")); if (dom.hasClass("plus")) { // 遞增 // 最大購買數量限制 if (_v === max) { return false; } now_v = (_v < 1) ? 1 : (_v >= max ? max : _v + 1); } else if (dom.hasClass("minus")) { // 遞減 // 最小購買數量限制 if (_v === 1) { return false; } now_v = (_v < 1) ? 1 : (_v > max ? max : _v - 1); } else { return false; } var cartId = dom.parents(".goodslist").attr("data-cashcoupon-id"); // ajax Cart.update(cartId, now_v, function (data) { // 更改數量 numDom.val(now_v); // 遞減數量按鈕 var minus = _t.find(".minus"); // 遞增數量按鈕 var plus = _t.find(".plus"); now_v > 1 && minus.hasClass("bg_gray") && minus.removeClass("bg_gray"); now_v === 1 && !minus.hasClass("bg_gray") && minus.addClass("bg_gray"); now_v < max && plus.hasClass("bg_gray") && plus.removeClass("bg_gray"); now_v >= max && !plus.hasClass("bg_gray") && plus.addClass("bg_gray"); // 計算總價 Cart.figurePrice(); }); event.preventDefault(); }); // 刪掉商品 $(".del").on(CLICK, function () { var dom = $(this).parents(".goodslist"); var cartId = dom.attr("data-cashcoupon-id"); cartId && Cart.update(cartId, 0, function (data) { // 提示 SD.Toast({'text': '刪除成功!'}); // 移除當先列 dom.remove(); // 計算總價 Cart.figurePrice(); }); }); // 刪掉全部商品 $(".delAll").on(CLICK, function (event) { SD.Confirm({ content: '你肯定要清空購物車嗎?', //lock: false, ok: { text: '殘忍清空', fun: function () { Cart.update('', 0, function (data) { // 提示 SD.Toast({'text': '刪除成功!'}); // DOM 操做 Cart.removeAll(); }); } }, cancel: { text: '再忍忍' } }); });
以 MV* 框架實現:Angular、Avalon)
經過後端接口獲取購物車數據,JS動態渲染頁面web
view:ajax
html
<div ms-controller="cart"> <div id="emptyBox" ms-visible="goodsList.length==0" style="display: none"> <p>購物車空空如也</p> <p><a href="#!/">去添加</a></p> </div> <div ms-visible="goodsList.length>0"> <div id="topTotal"> <span>商品總價(不含運費)<b>{{price|currency}}</b></span> <a href="pay.html" class="topBtn">去結算</a> </div> <div class="goodslistWrap"> <div class="goodslist" ms-repeat="goodsList"> <div class="imgWrap"> <img ms-src="{{el.cashcoupon.image}}" height="100%"/> </div> <div class="txtWrap"> <p>{{el.cashcoupon.name}}</p> <div class="handleBox"> <div class="numBox"> <span class="handle minus" ms-class-bg_gray="el.quantity<=1" ms-click="minus($index)">-</span> <span><input type="text" ms-attr-value="{{el.quantity}}"/></span> <span class="handle plus" ms-click="plus($index)">+</span> </div> <div class="trashBox" ms-click="remove($index,$remove)"> <i class="trash"></i> </div> </div> </div> <div class="priceWrap"> <span><strong>{{el.cashcoupon.price|currency}}</strong></span> <span><del>{{el.cashcoupon.mktprice|currency}}</del></span> </div> </div> <!--last--> <div class="clear"></div> </div> <div class="goodslistTotal"> <span>商品總價(不含運費)<b>{{price|currency}}</b></span> </div> <div class="bottomTotal"> <div class="delAll" ms-click="removeAll"> <i class="trash_b"></i> <span>清空所有</span> </div> <div class="payBtnBox"> <a href="pay.html" class="bottomBtn">去結算</a> </div> </div> </div> </div>
JS 業務代碼編程
javascriptdefine("cart", ["avalon", "request"], function (avalon) { var avalonAjax = avalon.ajax; var model = avalon.define({ $id: "cart", toggle: true, price: 0, // 總金額 goodsList: [], // 遞加數量 plus: function (index) { model.goodsList[index].quantity = parseInt(model.goodsList[index].quantity) + 1; // 計算總數量 和 總金額 count(); }, // 遞減數量 minus: function (index) { if (model.goodsList[index].quantity <= 1) { return false; } model.goodsList[index].quantity = parseInt(model.goodsList[index].quantity) - 1; // 計算總數量 和 總金額 count(); }, // 移除當前 remove: function (index, perish) { perish(); // 移除 }, // 移除全部 removeAll: function () { avalon.vmodels.cart.goodsList.clear(); // 計算總數量 和 總金額 count(); } }); // 獲取數據 var getData = function () { if (avalonAjax) { avalon.getJSON('', {method: 'ecoupon.cart.list'}, function (data) { model.price = data.totalAmount; model.goodsList = data.cartList; }) } }(); // 計算總數量 和 總金額 function count() { var _count = 0; var _price = 0; model.goodsList.forEach(function (goods, index) { _count += parseInt(goods.quantity); _price += parseFloat(goods.cashcoupon.price) * parseInt(goods.quantity); }); avalon.vmodels.page.cartNum = _count; model.price = _price.toFixed(2); }; // 購物車數量監聽(目前只能監聽商品種類變化, 不能監聽商品數量變化) model.goodsList.$watch("length", function () { count(); }); return model; });
zepto 版本中,js 業務代碼大量使用了 選擇器 來 操做DOM,致使 js 與 view 極度耦合。後端
avalon版本,利用avalon能夠大大加快咱們項目的開發速度,可使咱們遠離 DOM的世界,咱們的編程變成了只圍繞 model層而不圍繞 DOM,即操做 model就是操做 DOM,同時能讓咱們開發人員離開 DOM都能輕鬆進行前端開發。avalon中定義的 VM處理業務邏輯與提供數據源,HTML中的綁定負責渲染與響應用戶點擊拖拽等行爲,這樣就最大保證了視圖邏輯相分離。app
具體選擇就看場景了吧,動畫效果、特效多用 jQuery這種,業務複雜用 MV* 這種