京东火车票项目是一个不断迭代优化的项目。一想到要参与一个 GMV 很大的项目,内心抑制不住的兴奋。随着业务和需求的深入,兴奋感渐渐转变为压力。因为一直忙于完成业务需求,也就没有认真总结和反思存在的问题。借着写博客的机会,对项目进行简单的回顾和总结。

一开始接触项目是对当时存在的一些 bug 进行集中处理优化,对原有组件进行版本更新。然后到车次列表页进行前后端分离的尝试,还有对退改签业务的更新和重构。还有后来的学生票、儿童票丰富了火车票的业务场景。在这个过程中,遇到了形形色色的问题。有的问题很快解决了,有的就会耗费较多的时间精力。我遇到的问题有的是个例的,小概率的,有的可能是很多人都会犯的错误。我希望将一些自己认为有用的东西写出来。

学生票的特殊业务及其带来的问题

1、学生乘车时间为每年的6月1日到9月30日及每年的12月1日到第二年3月31日,可提前30天购买学生票,不能购买学生票的时候,学生票勾选不能点击。

TimLine图片20170612183639

看上去很简单的业务,做起来的时候发现还是不那么简单。因为它并不是简单的数字的比较。首先,要找出学生票可点击的区间范围,具体时间节点及范围如下:

  • 1月1日到3月31日
  • 6月1日到9月30日
  • 12月1日到12月31日

然后,重点来了,出发日期日历范围的控制。正常推算,这个范围就是从当天到30天后结束嘛,但是细想一下,这30天的区间跨度可能会包含了某个时间节点,就是说有可能30天后,学生票不在可购买的范围内。那么,日历的可勾选范围就并不是简单的从当天到30天后,就出现以下如图所示的几种特殊情况。区间图

思路理清晰了,转换为如下代码即可。

2、购买学生票填单页面需要输入学校和优惠区间城市,页面引入了学校和优惠区间城市的相应数据文件,但是学校和城市数据 JS 太大,导致页面第一次加载完成时间超级长,甚至加载到最后,直接失败,整个页面功能基本不能用。

20170612180709

解决方案:
因文件过大而导致的页面加载失败,最直接的解决方案就是缩小文件。
优化第一步:改变数据格式,删除冗余字段名,减小文件大小。具体修改如下图,改完后文件大小缩小了大概300多KB。

3

本以为这样就万事大吉了, BUT 问题来了,文件还有400多KB,网速稍稍不好的时候,刚进入页面时依旧会加载失败,导致页面功能无法使用。
优化第二步:按需加载。具体方式是,鼠标移动到需要该数据的区域时再请求数据。
这么做表面上看上去是解决问题了,初次进入页面超级快。BUT问题又来了,当鼠标移动到要操作的区域的时候,数据还没加载完了,用户就要操作了,来不及啊!
优化第三步:延时加载。具体操作办法是等待页面其他资源全部加载完毕,再加载学校的数据文件,这样利用用户在做不需要此数据的操作时,加载学校数据文件。如此,来不及加载数据的问题得到缓解,不浪费任何时间加载数据,同时也不影响用户的其他操作。
按照以上三不优化做完后,页面加载过慢问题有了很大的改善。

车次列表页前后端分离重构

起初是后端将数据直接输出到页面,可是有一个很奇葩的问题。就是在IE9下 table 代码段不能有换行,否则页面就会错乱掉。解决方法简单粗暴,后端将页面的换行回车都去掉。这样看似解决了问题,但引发了新的问题。就是 HTML 文件是一大坨难以名状的怪物,维护起来简直想去死。于是提出了前后端分离的实现思路。

后端只输出数据,渲染页面由前端处理。我使用的是 art-template 来进行数据渲染。

当时普通车次列表页和改签车次列表页同时开发。我对接了两个不同的开发同学,他们对业务有不同的理解,于是给出不同的数据结构。可是两个列表页只是业务场景不同, View 层的展示大同小异。最简单的解决方法就是开发同学统一数据结构,那我就只需要维护一套模板来渲染数据了。要不我维护两套渲染模板,这样带来的问题就是之后的维护成本会很高。就在前两种都不满足的情况下,想出了如下图新的解决方案。
list
数据处理函数,对不同的入口数据进行处理,输出需要的目标数据,我称之为是一种中间件模式。经过这层处理之后,对于后期开发同学数据结构需要变化的情况,只需要对数据处理函数进行修改,确保始终维护一份渲染模板代码。这样代码的稳健性和可维护性就提高了很多。

列表页会有很多个数据接口,有的是基本不会变动的,有的是需要开发人员更新的。将公用并且基本不会变动的接口放在公共接口对象里(前端控制)。可配置接口及可能变化的接口放在页面里,后台开发同学只需要将 HTML 本地开发的文件注释掉就可以了。
在使用 art-template 时出现一个问题,如何循环一个数字长度来显示大量重复控件。需要实现的效果图如下:
hotel
对应的数据结构如下图

1

需要实现的是根据 star 的不同数字展示不同的效果,10、20、30、40、50展示不同数目的星号,25、35、45展示对应的文字信息。

边界容错(undefined、try catch)

一开始约定好,拿到服务器返回的数据,返回了个 errMsg 。我简单粗暴的将它 alert(errMsg) 出来。这时用户接收到一个 undefined 弹窗。此时真的是强忍着不哭,童话里果然都是骗人的。

这些都是小 case,异常处理才是解决后顾之忧的最佳选择。之前写代码只会考虑到所有正常情况,也就对异常忽视了。随着经验的增加和需要提高项目的稳健性,需要考虑异常,并对异常进行及时处理。下面是一个异常处理的小demo。

业务效率提升(提取相同业务代码)

一开始总是容易被这样的业务逻辑欺骗,如果没有做好梳理,就会写成不同 DOM 操作进行不同的响应。如果此刻需要对响应2的业务逻辑进行修改,那么就会涉及到五处。正确的做法是将每个 DOM 操作,响应的响应2&响应3封装起来。这样修改的时候只需要修改封装的那一层,这样看起来是没什么难度。可在一开始写业务时,往往不能进行很好的拆分。何时拆分,拆分到多小。这些都是需要摸索和实践的。我管这个叫粒子化思想。不光是 JS 还有 HTML,保证每个 DOM 节点,每个函数我们都能单独操作,并且不会影响到其他的。
抽象重复频率高、功能相似的代码为组件, 将相同的业务代码抽离,使用时引入相同的文件。

在业务中如何提高自己

前几天看了一篇博客,“天天写业务代码的那些年,我们是如何成长过来的”。里面作者列的几个点是值得深思的,显然在火车票里我还没有达到这个要求。将内容贴出来,不断得进行回顾和反思。
工作一个月时:你打开 Backlog,看看需求卡,发现那张需要三个人天的卡,好像会更有挑战一些。
工作两个月时:你打开 Backlog,看看需求卡,发现完成这卡只是时间问题。
工作三个月时:你打开 Backlog,看看需求卡,发现清清楚楚地知道修改哪一行。

调试bug

在当下,我解决 bug 的能力还是偏弱的。尤其是不是自己写的代码。我分析了下原因,一方面是调试技巧的欠缺,一方面是遇到 bug 的心态。经过这么久的摸爬滚打,我总结了几点。首先复现问题,然后定位问题。从源头定位问题,而不是在问题发生后的某个阶段再去定位。找到问题之后,再去找合理的解决方案解决问题。
在定位调试 bug 时,往往会在页面写一些 console 语句。写得时候很爽,但上线的时候就惨了。需要对每个上线文件进行该语句的注释删除。工作量繁杂且无聊,组里的老司机给了下面的写法,看了之后,只疑惑自己为什么想不到这样的方法呢。

项目文件结构、把控上线风险

之前每次上线的时候,都会有点小紧张。总是害怕出现风险,后来采用了需求分批迭代,文件夹物理隔离,打日期戳的方法来避免这个问题。这个问题现在是解决了,但是随之而来又有了新的问题,就是某个页面的代码可能在多个需求中使用,这时候就会维护多份文件,维护成本增大。目前还没有想到好的解决方案,大家看到这儿有好的解决方案请留言。

作者:廖艳丽、胡颖超

喜欢(10) 评论(0) 分享

Leave a Reply

© 2014 JDC. All Rights Reserved.