近来离别运用 Zepto 和 Avalon框架写了个 SPA项目,贴出来议论下 JS DOM操纵为主 JS库 与 MV* 框架的对照
案例(MV* 框架 与 DOM操纵 JS库 实例对照)
购物车页面 JS营业逻辑(疏忽 Ajax要求–Ajax要求机遇依据产物详细情况而定)
- 以列表体式格局展现购物车商品
- 每件商品有”+”、”-” 按钮,点击该按钮则:
- 检测是不是到达购置极限(如:最小购置数目不能小于 1件)
- 到达购置极限则给该按钮增加相应的 class 以便提醒用户该按钮不能再点击
- 反之则去掉该按钮上的该 class(如:如今商品数目为 1,”-” 为不可用状况,如今点击 “+” 则 “-“变成可用状况)
- 改变该件商品数目
- 盘算 并 更新该商品总价金额
- 从新盘算 并 更新 购物车一切商品金额
- 移除单种商品
- 从视图上找到该 DOM
- 然后移除该 DOM
- 从新盘算 并 更新 购物车一切商品金额
- 移除购物车一切商品
- 显现购物车无商品 div,指导用户到商品列表等别的页面
完成:
完成一:Zepto 版
以 DOM操纵 JS库完成:jQuery、Zepto、MooTools)
经由过程后端控制器衬着页面
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 逻辑
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: '再忍忍' } }); });
完成二:Avalon版
以 MV* 框架完成:Angular、Avalon)
经由过程后端接口猎取购物车数据,JS动态衬着页面
view:
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 营业代码
javascript
define("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中的绑定担任衬着与相运用户点击拖拽等行动,如许就最大保证了视图逻辑相星散。
长处
- JS 与 view 解耦。阔别 DOM的天下,缭绕 model层
- 控制器、路由从后端放到前端,越发合适 Web APP开辟。SPA运用能够供应更好的用户体验
- 营业完成的体式格局改变(由直接操纵 DOM变成 操纵 VM,越发合适后端童鞋头脑体式格局)
- 更轻易完成前后端星散
- 官方讲代码量比 jQuery削减50%
- ….
瑕玷
- MV* 框架进修本钱高,观点多(控制器、路由、指令、过滤器、效劳、依靠注入…)[哈哈、后端同砚最喜欢这类了,进修起来无压力]
- 生长时候没有 jQuery这类时候长,组件比拟 jQuery相差比较大(大多数须要本身完成)
- 动画
- SEO(貌似有解决方案了)
- …
详细挑选就看场景了吧,动画结果、殊效多用 jQuery这类,营业复杂用 MV* 这类