2015阿里11.11:手淘敲钟项目总结
项目背景介绍
4号得到消息要做一个紧急项目,双十一当晚10点30分,北京水立方,美国纽约证券交易所为“2015天猫双十一全球狂欢节”举行远程开市敲钟仪式,见证这场全球商业的狂欢。到时马大大会和8位曾获得阿里公益“天天正能量”奖的人物代表举行远程开市敲钟仪式,这是纽交所首次为一家中国的互联网企业举行远程敲钟仪式。我负责在手淘上开发一个敲钟功能,让手淘的一亿多用户能够在手机上和马总一块敲响开市钟。
项目特点
快,时间紧迫,需要在短时间内完成需求以及应对各种变更 变,这种大老板拍的需求充满变数,因为pd觉得好还不行,还得大老板拍板。但是大老板们又很忙,没办法及时拍板。所以只能先按照PD的意愿做出来。心理和代码上都得做好快速应对可大可小变数的准备。 质,代码质量要求够高,减少bug量,缩短测试时间
项目开发过程
页面是放在poplayer里面展示的,什么是poplayer呢,它就是手淘在native界面上覆盖了一个新的webview容器,然后这个webview是透明的,透过这个webview还能看到底下的页面。这样需要在native页面上增加一些内容时就不用发版,更灵活。开发过程和普通手机浏览器里的页面是一样的,只需要在适当的时间调用手淘开放的方法打开和关闭poplayer。更多关于poplayer的介绍详见:#18
由于时间不够充裕,没办法等视觉稿出来再开发,所以前端和视觉同时开工。这个项目使用的是reactjs开发,react通过数据来控制DOM的特性,js里没有了对DOM的操作,让我们可以在脱离视觉稿的情况下进行页面逻辑部分的编码。 大致的开发过程如下。首先,根据需求内容将页面拆分成6个状态
/*
pageStatus:
SmallPreTip: 等待开始的悬浮icon
SmallStartTip: 开始抽奖的悬浮icon
Error: 错误页
CountdownWait: 显示倒计时等待页面
RefreshWait: 显示刷新等待页面
WaitAward: 显示抽奖页面
AwardSuccess: 中奖了
AwardFail: 没中奖
*/
每个状态对应于一个component,每个component里面通过props特性来和父级元素通信。
最终通过修改数据来控制页面的内容
switch(pageStatus) {
case 'SmallPreTip':
content = (
<SmallTip start="false"></SmallTip>
)
break;
case 'SmallStartTip':
content = (
<SmallTip start="true"></SmallTip>
)
break;
case 'Error':
content = (
<ErrorCom></ErrorCom>
)
break;
case 'CountdownWait':
content = (
<PopPreTip countdown="true" leftTime={this.state.leftTime}></PopPreTip>
)
break;
case 'RefreshWait':
content = (
<PopPreTip refresh="true"></PopPreTip>
)
break;
case 'WaitAward':
content = (
<WaitAward uid={this.state.uid}></WaitAward>
)
break;
case 'AwardSuccess':
content = (
<AwardSuccess></AwardSuccess>
)
break;
case 'AwardFail':
content = (
<AwardFail></AwardFail>
)
break;
default:
content = null;
break;
}
搭建好大的展示模型后,就是接口调用和结果处理了。在这个项目里,我将所有api调用放在一个js文件里集中处理。
param.doSee = {
api: 'mtop.taobao.aplatform.get',
v: '1.0',
ecode: 1,
data: {
bizType: 'zhong.go',
}
};
function mtopError(ret,errorCallback) {
errorCallback && errorCallback(ret);
}
var api = {};
for (let i in param) {
api[i] = function(){
var p = new Promise(function(resolve,reject) {
mtop.request(param[i],function(ret){
resolve && resolve(ret);
},function(ret){
mtopError(ret,reject)
})
})
return p;
}
}
也就是说,如果我需要调用新的api,只需在param里新增一项内容,无需再去重复编码mtop的调用部分,而且对于mtop的异常返回可以做一些共用的处理。 在需要调用api的地方,也不必太关注api的入参部分,只需处理返回结果就行。 mtop 是手淘这边封装好的调用接口的组件库,可以理解成一个普通的jsonp请求。
api.getPageStatus().then((ret) => _getPageStatusSuccess(ret.data,params),(ret) => _getPageStatusFail(ret,params));
最后根据接口的返回,根据需要控制pageStatus的值就可以。 将页面逻辑写完,待视觉稿出来,只需根据视觉稿逐一完成各子组件里的样式部分、动画部分和交互部分。
这种开发模式的优点除了可以和设计同行外,最主要的还是页面层级结果清晰,出问题是能够快速定位。这个项目,最终的开发时间是2.5d,6号开始开发,8号下午就提测了。视觉定稿是在开发的最后一天出来的,当然不可避免之后又修改了一版。 测试适配阶段几乎0bug,除了一些图片加载不出来我只能wontfix的bug,后期图片都是会使用手淘的zcache功能提前缓存到本地,可以避免这个问题。刚开始PM还担心适配会出现很多问题,害怕时间不够。但是自从使用了lib-flexible,基本适配无压力。lib-flexible的适配分成两种方案,dpr(根据设备的devicePixelRatio来设置不同的size,单位是px)和rem(根据屏幕宽度定义1rem=x px,然后单位用rem),基本弄清楚什么时候该用哪个就没问题了。坚持三个原则:
- 字号dpr,其他用rem
- 容器里如果有文字并且不能折行,容器相关的设置也应当用dpr
- 如果是那种文字必须在一行展示完,但是字又比较多的情况,则字也用rem。当然这种情况必须提前跟视觉沟通好,不建议自己单独使用,否则后面容易返工。
关于flexible的更多介绍,可以看一下大漠爷爷的这篇文章#17
总的来说,这个距离双十一不到一周的时间里提出来的项目还是挺成功的,10号就完成上线,距离11号开放入口还有一天的buffer,上线后也没有出现任何问题,直至下线。当然项目本身比较简单,然后PD的想法和大老板想法比较吻合,后期没什么变更。
高效的开发离不开工具,推荐使用以下几个工具:
- gulp-px3rem自动换算css里px到rem
- autoprefixer 自动给高级css3属性带上各种前缀,减少适配问题
- webpack&babel 转换jsx和es6语法,麻麻再也不用担心我用react和es6了