接触avalon差不多有一年时间了,当时是看前端大牛司徒正美的博客才了解到还有这么一个高大上的玩意,然后就加入了avalon的讨论群。从群里零零散散的了解了avalon的一些特性,感觉很强大,感觉思想比较超前,连jquery也成了他们的吐槽对象。司徒正美鼓吹使用avalon代码量比使用jquery至少少一半,不用操作DOM,更易维护,完全只需要操作Model数据层,界面会自动更新,当时听到这些概念的时候我也会思考某些功能不用jquery实现转而使用avalon实现的话怎么实现,当时还是有很多疑惑,觉得使用惯了jquery突然不使用的话都不知道怎么做开发了。当时也专门看过avalon的入门教程,由于在项目中用得少,很快就忘了。

在博雅,每天的活动任务量特别多,记得刚进博雅做的第三个活动是六合彩活动,因为一般活动开发时间都是3天左右(重构+交互),时间很紧,而且自己对六合彩规则一点都不懂,所以拿到这个活动,第一步就是熟悉规则,当时觉得这个活动挺好玩,应该不难,可是越做到细节就感觉需要考虑的地方特别多,交互特别多,界面更新特别多,于是大量的dom选择和操作充斥着整个开发,当时完成这个活动的时候js代码都达到了2000行,开发完成之后让我最觉得恶心的就是大量的dom操作,做什么操作都要去选择dom,然后根据dom进行操作,代码量一大,定位代码都很麻烦,像这样写的代码估计也就只能使用一次,没有可维护性。然后就突然想到了司徒正美的avalon框架,不用操作dom,当时觉得这应该是前端程序员最幸福的事情了,现在来看,如果当初使用avalon来开发这个活动,代码量最多也就1000行。于是,在后面活动的开发中,我是一边开发一边参考avalon入门教程,感觉越用越爽,开发活动速度越来越快,由之前的3天缩短到2天甚至更快。于是,爱上了avalon,开始有点厌倦jquery了。

这次公司内部的运营管理系统因为交互多,数据交互量大,针对这样一个比较庞大的系统,如果使用jquery的话估计又会被dom折腾死,所以我毅然选择了avalon进行开发。因为现阶段基本完成了游戏侧的开发,所以想总结下使用avalon开发项目的一些心得,一来让还没用过avalon的同事了解一下avalon与jquery的区别,二来也是给自己做一个项目开发的总结。

先来看看项目结构:

这里我用的是seajs模块化管理的库,引入jquery仅仅是为了使用ajax请求,util目录下是一些工具类,widget是一些ui组件,page下为相关的业务处理逻辑,这里因为分成游戏方和平台方所以用两个文件夹加以区分,calendar.js实现的是日历功能以及在日历上进行数据展示和相关业务操作,actlists.js文件实现的是下面活动申请列表的相关展示和处理。相关界面如下:

日历功能包括切换上一个月,切换下一个月,回到今天,而且展示当前月每天的活动申请量,在今天以前禁止申请,以深灰色标识,点击单元格不会弹出申请弹框,从今天到下周周末是限制申请,以浅灰色来标识,在用户点击单元格的时候会弹出一个dialog弹框,用户点击确定才弹出活动申请弹框,点击取消关闭dialog弹框,其余的是可以申请的,点击单元格弹出活动申请弹框。

首先,为了展示这样一个日历,我定义了一个数组,将当前月,上个月的后几天,下个月的前几天都放在了这个数组里,数组里没一个元素都是一个对象,用来记录当天的信息,比如年,月,日,是否是今天,是否被限制,是否被禁止,是否是当前月,当日的时间戳,以及当日的活动申请量。看代码:

function getWeeks (ooo, appList) {
var dateObj = new Date();
var todayTime = new Date(dateObj.getFullYear(), dateObj.getMonth(), dateObj.getDate()) - 0;
var todayDate = dateObj.getDay();
var year = ooo.getFullYear();
var month = ooo.getMonth(); //得到今天是几月(0 ~ 11)
var date = ooo.getDate(); //得到今天是几号 (1 ~ 31)
var cur = new Date(year, month, date);
cur.setMonth(month + 1); //改为下一个月
//由于日期是1 ~ 31, 0则是退到上一个月的最后一天,于是得到这个月的总天数
cur.setDate(0);
var num = cur.getDate();
var next = 6 - cur.getDay();
var dates = avalon.range(1, num + 1);
var currentDay;
dates = dates.map(function(d) {
return dateObj.getFullYear() === year &&
dateObj.getMonth() === month &&
date === d ? {
year : year,
month : month,
date : d,
num : appList[d],
flag : true,
isToday : true,
time : new Date(year, month, d) - 0
} : {
year : year,
month : month,
date : d,
num : appList[d],
flag : true,
isToday : false,
time : new Date(year, month, d) - 0
};
});
cur.setMonth(month);
cur.setDate(1); //得到当月的第一天
var prev = cur.getDay(); //0 ~ 6
cur.setDate(date); //还原
for (var i = 0; i < prev; i++) {//补上上一个月的日期
var curr = new Date(year, month, -1 * i);
dates.unshift({
year : year,
month : month - 1,
date : curr.getDate(),
flag : false,
isToday : false,
time : curr - 0
})
}
for (i = 0; i < next; i++) {//补上下一个月的日期
curr = new Date(year, month + 1, i + 1);
dates.push({
year : year,
month : month + 1,
date : curr.getDate(),
flag : false,
isToday : false,
time : curr - 0
})
}
for (i = 0, len = dates.length; i < len; i++) {
currentDay = dates[i];
if (currentDay.time >= todayTime) {
if (currentDay.time - (14 - todayDate) * 24 * 60 * 60 * 1000 < todayTime) {
currentDay.isLimit = true;
} else {
currentDay.isForbid = false;
}
} else {
currentDay.isForbid = true;
}
}
var ret = [];
while (dates.length) {//每行七个分组
ret.push(dates.splice(0, 7));
} return ret; //一个二维数组
}

返回的数据格式如下:

获取到数据之后,只需要利用avalon的动态模版引擎直接渲染就生成了上面的日历,看模版代码:

<tr ms-repeat-el="calendar">
<td ms-class="day_style:elem.flag==true" ms-class-1="day_gray:elem.isLimit==true" ms-class-2="day_darkGray:elem.isForbid==true" valign="top" ms-repeat-elem="el" ms-click="clickApply(elem)">
<div class="day_mouse"><div class="day_no" ms-if="elem.isToday!=true">{{elem.date}}</div><div class="day_no" ms-class="today:elem.isToday==true" ms-if="elem.isToday==true">今天</div><div class="day_main"><p ms-if="elem.num>0">{{elem.num}}个资源申请</p></div></div>
</td>
</tr>

这个ms-repeat-el="calendar"中的calendar就是上面的二维数组,然后针对模版进行了二次循环输出,里面有大量的以ms-开头的是用于处理模版逻辑的,比如ms-class-1="day_gray:elem.isLimit==true"就是当前日对象isLimit为true时就加上day_gray类名,其他代码属性大家可以去司徒正美博客了解下哈。针对切换上一个月,下一个月,回到今天的功能我们只需要操作calendar这个数组就行,完全的操作model数据层,没有任何dom出场的机会,这样的代码维护性高,功能划分清晰,给人一种特别愉快的开发感受。

同样的,针对下面活动列表展示和过滤我们也是将所有的活动放在了一个数组里,然后针对数组进行模版渲染,看代码:

//活动列表展示及过滤
var actListObj = avalon.define('applyActLists', function (vm) {
vm.actlists = []; //申请过的活动列表
vm.$actlistsClone = []; //申请过的活动列表副本
vm.status = -1; //活动过滤字段,-1为全部状态
vm.filterActListCallback = function (val) {
vm.status = val;
switch (val) {
case '-1':
actListObj.actlists = actListObj.$actlistsClone;
break;
case '0':
tools.filterActLists(actListObj.$actlistsClone, 0);
break;
case '1':
tools.filterActLists(actListObj.$actlistsClone, 1);
break;
case '2':
tools.filterActLists(actListObj.$actlistsClone, 2);
break;
case '3':
tools.filterActLists(actListObj.$actlistsClone, 3);
break;
}
};
});

相应的模版代码:

<div class="cur_progress" ms-controller="applyActLists">
<div class="title">
<div class="left"><b>我的活动申请的进度:</b></div>
<div class="right">
<span class="green_block color_block"></span><span>审核已通过</span>
<span class="yellow_block color_block"></span><span>审核中</span>
<span class="red_block color_block"></span><span>审核被驳回</span>
<span class="white_block color_block"></span><span>待提交</span>
<select id="s1_text1_bold" ms-duplex="status" data-duplex-changed="filterActListCallback">
<option value="-1" selected>全部状态</option>
<option value="0">待提交</option>
<option value="1">审核中</option>
<option value="2">审核被驳回</option>
<option value="3">审核已通过</option>
</select>
</div>
</div>
<dl>
<dd ms-repeat="actlists" ms-class="odd:$index%2==1">
<div class="top"><b>{{el.actName}}</b></div>
<div class="bottom">
<a href="javascript:" class="a_submit a_act" ms-class-1="a_act_yellow:el.step==1&&el.status==1" ms-class-2="a_act_red:el.step==1&&el.status==2" ms-class-3="a_act_green:el.step>1" ms-click="show_apply_detail(el)">提交审核</a><span class="g_dot"></span>
<a href="javascript:" class="a_self_test a_act" ms-class-1="a_act_white:el.step==2&&el.status==0" ms-class-2="a_act_green:el.step>2">自测通报</a><span class="g_dot"></span>
<a href="javascript:" class="a_scheduling a_act" ms-class-1="a_act_green:el.isScheduled==1" ms-class-2="a_act_yellow:el.step==3&&el.status==1">查看排期详情</a><span class="g_dot"></span>
<a href="javascript:" class="a_effect a_act" ms-class-1="a_act_green:el.isOnline==1" ms-class-2="a_act_yellow:el.step==4&&el.status==1">查看上线效果</a>
</div>
</dd>
</dl>
</div>

活动申请的过滤功能是在select上绑定了change事件的回调函数filterActListCallback(data-duplex-changed="filterActListCallback"),当select的值发生改变时,针对select的当前值返回新的活动列表数组,然后界面就会自动更新,如下:

我们看到,利用avalon可以大大加快我们项目的开发速度,可以使我们远离dom的世界,我们的编程变成了只围绕model层而不围绕DOM,即操作model就是操作dom,同时能让我们开发人员离开DOM都能轻松进行前端开发。avalon中定义的VM处理业务逻辑与提供数据源,HTML中的绑定负责渲染与响应用户点击拖拽等行为,这样就最大保证了视图逻辑相分离。

使用mvvm框架avalon开发公司内部运营管理系统的一些心得的更多相关文章

  1. 轻量级前端MVVM框架avalon - 初步接触

    迷你简单易用的MVVM框架 avalon的介绍http://rubylouvre.github.io/mvvm/ 按照作者的介绍,在HTML中添加绑定,在JS中用avalon.define定义View ...

  2. MVVM框架avalon在兼容旧式IE

    迷你MVVM框架avalon在兼容旧式IE做的努力 当前标签: avalon 共3页: 1 2 3 下一页  迷你MVVM框架avalon在兼容旧式IE做的努力 司徒正美 2014-03-13 11: ...

  3. 前端MVVM框架avalon - 模型转换1

    轻量级前端MVVM框架avalon - 模型转换(一) 接上一章 ViewModel modelFactory工厂是如何加工用户定义的VM? 附源码 洋洋洒洒100多行内部是魔幻般的实现 1: fun ...

  4. 轻量级前端MVVM框架avalon - 执行流程2

    接上一章 执行流程1 在这一大堆扫描绑定方法中应该会哪些实现? 首先我们看avalon能帮你做什么? 数据填充,比如表单的一些初始值,切换卡的各个面板的内容({{xxx}},{{xxx|html}}, ...

  5. 轻量级前端MVVM框架avalon源码分析-总结

    距avalon0.7版本发布有一段时间,由于之前的稳定性,就停止一段时间更新,期间研究了下Knockout源码,也尝试写了一个小型的mvvm的实现模型,仅仅只是仿造ko的核心实现,把无关的东西给剥离掉 ...

  6. 使用MVVM框架avalon.js实现一个简易日历

    最近在做公司内部的运营管理系统,因为与日历密切相关,同时无需触发条件直接显示在页面上,所以针对这样的功能场景,我就用avalon快速实现了一个简易日历,毕竟也是第一次造日历这种轮子,所以这里记录下我当 ...

  7. 轻量级前端MVVM框架avalon - 执行流程1

    基本上确定了avalon的几个重要元素的关系: M,即model,一个普通的JS对象,可能是后台传过来的,也可能是直接从VM中拿到,即VM.$json.有关的这个$json的名字还在商讨 V,即Vie ...

  8. 轻量级前端MVVM框架avalon - ViewModel

    废话说了大几篇,我们开始来点干货了~ ViewModel的内部机制 在MVVM中,数据是核心.而jQuery则以DOM为核心. 而DOM只是HTML在JS的世界的抽象,是一个很易变的东西.因此如果业务 ...

  9. 轻量级前端MVVM框架avalon - 整体架构

    官网提供架构图 单看这个图呢,还木有说明,感觉有点蛋疼,作者的抽象度太高了,还好在前面已经大概分析过了执行流程 如图 左边是View视图,我们就理解html结构,换句话就是说用户能看到的界面,渲染页面 ...

随机推荐

  1. WHMCS系统API调用

    WHMCS:域名管理系统,现在网络上很多借助此系统Shadowsocks插件+ShadowsocksR多用户服务端进行VPN的售卖,能做到流量控制等. 在对接此系统的API时,我发现了很多功能都已经实 ...

  2. Visual Studio 2015出现Cannot find one or more components. Please reinstall the application.的问题解决

    cd C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE devenv.exe /resetuserdata devenv. ...

  3. PHP之:多图上传

    撰写日期:2016-6-30 15:17:35 Thursday 参考 http://a3147972.blog.51cto.com/2366547/1381136 (08-05ThinkPHP+sw ...

  4. 捉襟见肘之 CoreImage初级自制相机图片效果

    CoreImage.framework /* CoreImage - CoreImage.h Copyright (c) 2014 Apple, Inc. All rights reserved. * ...

  5. Matlab小技巧

    记录一些用Matlab的技巧. //imshow全屏 subplot(1,3,3); imshow(topSketMat); hold on; set(gcf, 'units', 'normalize ...

  6. Git删除tag

    git tag -d v2016062101 删除本地tag git push origin --delete tag v2016062101 删除远程tag

  7. Hibernate学习总结

    首先声明这是个坑爹的框架 属于ssh经典框架中的持久层框架,说白了就是管理数据库的. 下载地址:http://hibernate.org/orm/ 这里写了版本5.2,下载下来的基本不怎么会用,因为文 ...

  8. Linux 的cp命令详解

    功能: 复制文件或目录说明: cp指令用于复制文件或目录,如同时指定两个以上的文件或目录,且最后的目的地是一个已经存在的目录,则它会把前面指定的所有文件或目录复制到此目录中.若同时指定多个文件或目录, ...

  9. nginx+php-fpm的socket配置小结

    关于socket的介绍本文不再赘述,生产环境中常用socket方式,本文简述其配置方式. #cd /app/local/php#切换到php安装目录下 #mkdir run #chmod 777 ./ ...

  10. POI导出Excel的空值问题

    情景: 页面上的表格导出Excel,表格没数据导出的空格也要参与运算,结果在Excel上出错了 看图, 行-A.A12是没有数据的,后台代码cell.setCellValue("" ...