Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

年会祝福+抽奖程序前端设计 #5

Open
Alexis374 opened this issue Feb 25, 2016 · 1 comment
Open

年会祝福+抽奖程序前端设计 #5

Alexis374 opened this issue Feb 25, 2016 · 1 comment

Comments

@Alexis374
Copy link
Owner

贵司2016年年会抽奖的任务分派给我了,需求如下:

  • 现场会有人从内部聊天工具发送祝福语,服务端提供http接口,前端取得数据进行展示,类似人人墙的效果。
  • 给定参会人员抽奖名单,从中抽取一等奖,二等奖,三等奖等奖项,并展示效果。

需求看起来简单,但是经不住需求人员反反复复变更的需求啊,直到上线前一天,还否定了之前的设计,重新找人设计了图,更不用说抽奖规则之类的了。。。
接到需求之后,我认真研究了一下,具体需要注意以下几点:

  1. 时效性短,应用范围窄。仅需一台电脑打开网页,展示页面,然后投放到大屏幕上,不用考虑浏览器兼容性问题;
  2. 需要考虑安全性问题。后台的接口是不做任何过滤的,如果有人恶意地注入js脚本,比如弹个框什么的,阻塞页面展示,直接GG。
  3. 抽奖工作在浏览器端做,应该在页面加载的时候就一次性做好,而不应等到点击按钮时再做,否则会出现卡顿的情况。
  4. 对抽奖人员名单要做好去重处理,已经中过一次奖的人不能再次中奖。

我列出的技术方案如下:
1. 用Vue.js做前端MVVM框架,展示页面
2. 用lodash做列表筛选,随机选择指定的人数,作为中奖人员,且进行去重处理。
3. 用xss.js做xss过滤
4. 抽奖页面和显示中奖结果页面是两个页面,抽奖的计算是在抽奖页面,在这个页面计算后,存入localStorage里面,中奖结果页面从中取出结果并显示。
5. 祝福页面,需要定期清理已经展示过的祝福语,减少浏览器性能损耗。不过用了Vue之后,将要显示的数据和Model层双向绑定,清除过期数据只需更改代表数据的数组即可。

具体实施过程以及部分注意事项:

  1. 表情包解析:
  • 所发祝福里面可以含有表情,类似“/:snbye”,这种形式,对应一个文件名,需要解析出文件名,拼接出地址,请求出图片。有个表情和图片名称对应的关系,是xml格式的。前台js不方便解析xml,我首先用python将这种对应关系转成json形式的。主要用了BeautifulSoup解析xml,再写入json文件。
  • 页面加载时,在初始化Vue对象时在生命周期的compile阶段将这些json文件的内容请求并缓存为改model的emotion_list对象。
  • 对于每一条信息,遍历emotion_list,截取出表情,转成文件名,并拼接出img标签。这一步写成了filter形式,可以直接在模板中调用。
  • 需要注意的是虽然Vue默认会过滤html标签,但因为img标签是动态生成的,必须动态插入,会留下安全隐患。因此,这一步可以借助xss.js的白名单机制,只允许img标签的src属性,生成过滤规则,应用在动态生成的img上。
  • 获取祝福语并展示
  • 因为页面上只展示3,4条数据,展示过的数据需要删除,而一次请求20条数据。我设置了两个数组,people数组一个与页面上的模板绑定,用于显示数据,另一个是tmp数据,作为取得数据的缓冲。
  • 当tmp内有数据时,将tmp的数据插入到people头部,但当people的长度大于4时,则将后面的people[4]后的所有数据清除。 当tmp的length小于40时,向服务器获得新数据
  • 以上策略需要两秒钟展示一次新数据,所以需要循环2s执行。但setInterval长时间执行后,间隔就会越来越失真,所以我利用setTimeout改写如下:
        function interval(lastId){

        var flag = setTimeout(function(){
            clearTimeout(flag);
            //长度大于40 直接展示页面
            if(vm.tmp.length>40){
                callback.call(vm,2);
            }
            //请求数据 展示页面
            else{
                request(lastId,callback,function(err){console.log(err);}) 
            }
        },2000)
        }

然后在callback里再次调用interval函数,以实现间隔调用

  • 为防止系统出现错误刷新后又从第一条开始显示,我将每次请求的lastId保存在localStorage里,以保证下次请求从这个id开始。
  • callback里做的事情主要是判断tmp长度,大于40直接展示,或者不足40时,根据请求到的数据,往tmp里添加
  • 在实践过程中,我首先用了Vue的vue-resource库发送jsonp请求,但在请求次数多了以后,vue-resource虽然能发送成功数据,但是无法调用回调,一直处于pending状态,可能是实现上有bug。不得已换用jquery的ajax,没有出过问题
  • 动画效果
    动画效果前期折磨了我好长时间。如果直接用jquery操作DOM的话,那么写动画效果很简单的。但是现在模型层和模板绑定,people的第一个元素就是模板li的
    第一个子元素,而且由上可知,新数据是由tmp插入people的第一个元素,然后原本是第一个的数据现在变成第二个,以此类推。而再向其应用动画效果,很容易
    导致错乱。经过多次尝试,我利用了Vue的过渡系统,在过渡开始的时候找到元素,及其兄弟元素,应用jquery在1s内改变其top值。刚好能达到效果。但这次算是
    误打误撞地成功了,因为改变时间间隔,又会导致动画错位。若因为动画的原因不能成功,那么只能放弃使用Vue,而是老老实实直接操作DOM
  • 抽奖
    抽奖完成的操作主要是筛选,去重等。lodash封装了各种对象,数组操作的方法,很容易就实现了所需的功能。如_.sampleSize,_.differenceWith等。

总结:
这个项目是我将Vue应用于实战的第一个项目,并没有用到高大上的组件等功能。从这次项目中我体会到了只看文档是永远停留在纸上谈兵的阶段,唯有真刀真枪应用于实战,才能提高自己的水平。
同时,没有哪种技术是万能的,要根据自己的需求选择合适的技术。这就需要涉猎各种技术的同时,能够取其精华,分辨其应用场景。
ps: 因为做了这个,给了个正激励200,啊哈哈。。。

@Alexis374 Alexis374 added this to the blog context milestone Feb 25, 2016
@BuptStEve
Copy link

talk is cheap~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants