接触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. 洛谷P1726 上白泽慧音

    题目描述 在幻想乡,上白泽慧音是以知识渊博闻名的老师.春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄.因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点.人间 ...

  2. F7控件

    //动态给F7加过滤 waf("#costSubject").wafPromptStandard("option", "filteritem" ...

  3. AngularJs $compile编译服务与指令

    $compile 这是个编译服务.编译一段HTML字符串或者DOM的模板, 产生一个将scope和模板连接到一起的函数. 编译服务主要是为指令编译DOM元素,下面的一大段也是主要介绍指令的. 下面是一 ...

  4. TortoiseSVN 过滤文件(包括已提交和未提交)

    一:svn 设置过滤文件方式 1.选中需要过滤的文件夹或者文件---右键---TortoiseSVN---Add to Ignore list(如果不显示说明该目录已经被添加) 2.在当前工作区域 不 ...

  5. hdu 2037 - 今年暑假不AC(区间调度问题)

    题意:区间调度问题 解法:应用贪心算法,贪心的规则: 在可选的节目中,选取结束时间早的节目. 1: #include<stdlib.h> 2: #include<string.h&g ...

  6. 【Alpha阶段】第二次Scrum例会

    燃尽图软件存在bug,正在排查修复:(已修复)由于时区设置到了美国,图表显示有问题. 会议信息 时间:2016.10.18 22:00 时长:1h 地点:大运村1号公寓5楼楼道 类型:设计阶段阶段性会 ...

  7. JSF JQUERY 使用datepicker

    不推荐使用.可以用primefaces的p:Calendar替代,更换控制使用. 简单使用jquery的datepicker示例: <!doctype html> <html lan ...

  8. JavaWeb学习总结-05 Servlet 学习和使用(01)

    一 Servlet的原理 1 Servlet 的创建 当Servlet容器启动web应用时,需要立即加载Servlet时: Servlet容器启动web应用时,将按照指定的顺序初始化Servlet,需 ...

  9. java编程思想-java中的并发(二)

    二.共享受限资源 有了并发就可以同时做多件事情了.但是,两个或多个线程彼此互相干涉的问题也就出现了.如果不防范这种冲突,就可能发生两个线程同时试图访问同一个银行账户,或向同一个打印机打印,改变同一个值 ...

  10. lunix的查看Tomcat目录下日志的快速操作

    可以使用cd命令,cd命令的功能是切换到指定的目录: 命令格式:cd [目录名] 有几个符号作为目录名有特殊的含义: "/"代表根目录. ".."代表上一级目录 ...