没事自己写了一个移动端日期选择器(三排,上下滚动来选择年月日的那种),还放到了公司项目中,
一开始还是一个小小的 只要选个日期就好,
实现难点:
1:touchmove滚动,然后选中相应的日期(年月日);
2:滚动时切换年月时对应的级联变化(闰年非闰年在二月有变化,大月小月之间日期也会有变化);
3:级联变化导致视图更新后,将选中元素归位(比如原来选中31号现在切换到小月,31号不能选,需调整到30号);
由于本文不介绍实现(主要是优化)
故仅仅带过其他实现:
(滚定位置与选中元素的关联,滚动高度/单个元素高度 = 显示的列表第n项),
(级联变化的需要更新视图,监听选中元素变化即可),
(元素归位,滚动的高度不能大于容器高度-1个元素的高度),
设计渲染试图的数据如下
1.{
yea: [{num:2018},...],
mon: [{num:0},...],
dat: [{num:1},...],
}
这样得到一个比较小的数据量的数据结构;
还能更小? (mon 的 1-11 在 dat里面有,可以引用,这样索引总比新对象占的资源少)
这种结构在切换年月时,不需要重新生成渲染数据,但是需要对相应日期数据做显隐控制(比如二月没有30 31 号),
然后还得到有一种比较大的数据结构;
2:[{
num:2018,
list: [{num:1 , list: [{num: 0}]},...]
},...]
这种数据结构可以在生成时,控制好数量,无需做显隐控制,但是数据量非常大,比如生成80年可选的日期,会生成 大概 80*12*30 = 28800 个对象,
相比 80+12 +31 = 123 个对象来说,资源消耗是相当的大;
一开始选择了第一种数据结构;
因为一开始的目标是 渲染从某年开始往后n年的日期选择,
然后写个方法判断当年是否是闰年,
然后写个方法判断当月是否大小月,
然后两个方法一组合,就可以计算出当月有多少天,
然后和日期对象的num属性做比较就可以知道显隐关系;
后面加了一需求,需要指定日期范围,这样就导致不仅要计算日期的显隐,包括月份显隐,
然后感觉这样子写有些笨笨的(vue 等框架的数据驱动是这样子用的吗,好像有点不是);
然后切换到第二种数据结构,
不考虑初始化性能,初始化之后的逻辑确实简单了许多,因为将复杂度解耦出来了,
touchmove的时候,选中了哪年就拿哪年的数据去渲染,什么事都好办了不是(切换月份同理);
然后到了测试那就出事了,在某些机型上(没错就是苹果),多渲染几次就崩溃了;
嗯毕竟移动端都是单页面应用,并且每个页面使用的日期数据不一定一样,所以每个页面(或者重新进入页面)调用会重新生成渲染数据;
然后就想办法优化呗,改回去是不可能的了,
那就拿它俩融合下呗,
年份列表是不太可能复用的
(性能主要不是它,再多也不过就是100,嗯选个出生日期还能过百?再不行给个200?)
那就从月份开始吧,
每年都是12个月没得跑了,区别就是闰年和平年区别的了,两个数组二选一吧;
然后是日期,每个月份有 28 或者29 或者30 或者31 天;
那行, 来个四选一;
嗯嗯,其他要用的时候,就来拿索引吧
比如渲染2018 开始以及往后九年的共十年的数据;
那么 除了 2020 2024(应该没漏吧)是引用闰年的月份数组外,
其他都是引用的平年的月份数组,
然后日期的还可以优化,
四个数组里面的对象都是来自最长的那个
比如 datArr31= new Array(31).fill(0).map((item,index)={num: index+1})
那么 datArr30 = datArr31.slice(0,30),其他同理。
在实现某日前不能选取的时候,稍微麻烦一点,需要重新生成该年的渲染数据(除了日期对象,其他都不能复用,比如将某月的前几天去掉了,然后其他实例引用时,该月的前n天就米有了,年份引用月份时同理),
现在回过头来算算性能,
虽然达不到最初的那种数据结构的性能,
但胜在将复杂度解耦出来了(数据生成与业务逻辑之间解耦);
如果看过JavaScript忍者秘籍里的好像叫做函数记忆吧,
润平年的月份数组,28 29 30 31 的日期数组等可以放到生成数据的方法上(在执行时生成后绑定到方法的属性上(什么,还有给方法加属性这种操作?)),
这样各页面(或重新进入页面)时,无需重新生成这些东西;
也就是假设80年 80+ 12*2 +31 = 135 个对象(由于第一年的月份对象也不能复用 那再加上 12 = 147),
然后加上 建立索引的数组了 (80*12 =960), 差不多1200 够了吧,
相比原来 近3w 来说效果还是很显著的;