table 如何更优雅的 reload(第二版)

讨论 未结 置顶 精帖
110 4547
岁月小偷
悬赏:80飞吻
最近小版本发的勤,这个是好事,但也带来了一个烦恼的就是以前以前发过的一些帖子的处理,比如这个table的reload的相关处理,以前帖子里面提到的修改的地方在新的版本上修改之后应该就可以使用了,但是后面做了tablePlug之后也一直在考虑如何在考虑如何在不修改源码的基础上去实现,但是最终考虑下来发现还是没办法(估计也是自身能力的限制),考虑到性价比的问题,最终通过只改动一处地方,后面细节处理上使用“打补丁”的方式实现了。(有小伙伴几次问了进度,也鸽了几次[挖鼻] ,请见谅)
有小伙伴说看了一头雾水进来出去,这里先补充一下,关于这个修改的起因和作用。如果你是个资深的layui的使用者,或者是一个很在意细节的人,或者甲方抠的很细...都会很容易遇到的一个问题是当前的表格的重载的体验不好的问题,俗话说就是“闪得厉害”、“晃眼”之类的,实际上这个是因为目前table的reload的设定,他是一个重载,内部的实现逻辑实际跟重新render一下是没太大的区别的,只不过reload的时候可以继承以前的属性,不用写那么多而已,但是背后的实际执行的还是一次render。render做了什么操作,reload就一样的,会重新新建整个tableView,也就是说页面会用新的dom去替换掉以前的dom,这就是“闪”的根源,那么如何不闪?
使用table内部的page分页组件切换页面的时候就会觉得“对!要的就是这个效果”,那么实际就是说其实table内部是有这种逻辑的,那么reload是不是也能使用这个逻辑来重新加载数据就好,那么这个时候就多了一个概念了,一个叫做重新加载数据,一个叫做重载,这个时候不能太左,就把reload都做成跟page一样的,实际上重载也是需要的,关键是区分什么时候需要重新请求数据,什么时候是需要重载一下的,比如你只是换个查询条件where,换个页码curr,换个排序条件,换个接口等等这些数据接口相关的,实际上就是一个重新请求数据的范畴,那么如果你切换了是否需要分页,切换了是否显示totalRows,重新设置了toolbar,这些都会影响这个架子的,这个就是一次重载的范畴了。
所以下文说的就是如何充分利用table的内部逻辑,将table.reload在实际的应用中将区分开来,让我们在实际的使用中如果只是一些普通的查询等数据相关的reload的时候变得不那么晃眼也就是标题提到的“优雅”。当然以前一开始的时候也考虑过要不多加个requery或者search之类的,但是很快就否掉了,这样子无形中又多加了很多的方法出来,虽然好处也有,就是比较明确,但是会造成要大面积的去看去修改以前的代码,所以一个reload就能够搞定的事情,就不要再添加更多的方法来掺和了,这样子修改 以后以前的代码几乎是不需要任何变动的。
更新于2018年11月6日16:35:50
附带的一个修改,因为table源码修改把thisTable透漏了出来,它能处理的事情可能超乎你的想象,比如目前有一些小伙伴提到的一个问题,就是加载的时候没有显示表头数据,只有等到数据回来了之后内容渲染出来了之后才会显示表头出来类似于:

这个的原因还有处理方式以前发过的一个帖子 https://fly.layui.com/jie/33536/ (加强部分的第3条)里面有提到过,但是如果使用了tablePlug呢?实际上只需要在这里添加两句代码:

效果是:

有木有觉得很方便,可以不用修改更多的源码的基础上就能够修改一些原先table存在的问题。[微笑]
更新于2018年11月7日18:00:09
发现了两个白名单的漏网之鱼initSort和autoSort。
修改优化loading的效果,首先上面的修改之后表头会在加载的时候就出来了,不会一开始空白然后等到渲染完数据之后再显示,那么又有小伙伴提出来,loading显示在标题里面了,比如:

这个是因为目前loading的逻辑就是放在laybox里面这个box包括头部和body,如果你设置了tbale的高度,而且高度还行的时候看着这个loading还是可以接受的,但是如果你设置表格高度自动,那么一开始body没有高度,那么box的中间就变成了表头的中间了,实际上效果就觉得不是很合理了,再加上个细节,亲们你们的图标会动吗[偷笑]
那么在tablePlug如何改进它?如下:

效果:
动图录起来费时费力因为社区上传文件大小限制,经常为了录一个效果录了好多次,所以还是不以动图为主了,直接自己在这个测试连接上测试测试就可以了。
测试界面简介

现在默认是开启“智能重载模式”的,(姑且这么叫吧)可以通过上面的开关去启用或者关闭这个功能,然后也可以通过右边的开关去控制单个表格是否要这个功能,然后就可以对比原始的跟开启之后的区别了,闪是一方面,也可以在input中输入内容看看会不会被重置之类的这些做效果的对比。

如何使用:
1、还是跟上个帖子一样,在tablePlug模块里面实现的,所以下载对应的文件,项目页面中use tablePlug。

2、考虑到不是所有人都需要这个功能,所以做了一个总开关,默认不启用,需要给启动后面才能使用。见上图中间部分:
tablePlug.smartReload.enable(true);
3、然后给需要使用这个功能的表格设置smartReloadModel:true(ps.如果你想要默认表格都是这种形式的,用table.set去设置全局的默认配置)

4、table.reload这个不需要任何改动,组件会根据你reload里面的参数去判断是重新请求数据还是重载
4.5、如果这时候发现没效果,那么请看看浏览器信息,是不是报了一个红色的信息,这个就是上面说的,这个改动需要一个前提,改一点源码,详见5

5、修改源码或者使用测试项目中给的table.js(版本2.4.5)
实现原理以及相关的可用的方法:
※首先是修改源码让它把一个关键的内容透漏出来(这个是本次修改必须的前提),方便后面我们去根据自己的需求去修改里面的一些逻辑来达到目的,有一些修改是可以不需要内部的逻辑的,比如上一个帖子里面提到的那些功能(复选状态记忆,筛选状态缓存)这些强化的拓展功能,可以不需要动原始的内容,利用目前已经有的接口方法等就能够搞定了,但是有一些功能比如这个reload的时候根据情况支持不重载的,还想要用reload,然后它内部的逻辑就是跟重新render没太大区别的重载,所以得改造,但是如果让我不改源码直接全部重写逻辑,估计一方面代码量会很大,即使做出来效果ok了感觉也很table本身是脱钩了变成两组件了,那何不把关键的一些东西透漏出来然后充分的利用内部的方法去实现呢?
※然后就是重新整理reload的逻辑(这个是本地修改的第二个重点),主要内容大致如下:

基本思路就是缓存原始的reload逻辑,重写table.reload在里面去判断出是哪种模式,如果是重新请求的就调用table内部的重新请求的方法,如果是重载的就走原始的前面缓存起来的reload的方法。然后如果是重新请求的话记录一个状态,后面需要“打补丁”
※最后是打补丁(这个是本次修改的第三个重点,也是一个比较耗时的细节处理),为啥需要打补丁,打补丁打的是啥?实际上这个就是因为原始的reload是一个重载,每次都是一次重新render的过程,所以内部的一些很细节的处理实际上是比较简单粗暴的,如果你要使用的是重新请求的,那么也就是说很多东西应该是高复用的,这里举几个例子,大家也可以尝试一下,把上面reload的里面添加打补丁的状态的那一行注释掉,然后再跑一下测试看看,在各种出错情况下页面的效果,还有出错之后重新reload的时候正常了又变成啥样子了。

下面举几个例子,都是在满足重新请求下的reload的前提下:
场景一:如果reload的时候接口异常了。分析:这是因为原始的reload重载,整体的节点会重新生成,也就是说这些节点一开始是没有的,后面会根据数据去生成,如果出错的话这些节点就不会有,只会有中间那句数据接口异常:error之类的错误提示信息,那么重新请求在这种情况下就得自己处理下这种情况了,所以需要打补丁,当然也可以修改原始的代码逻辑,第一版就是这么做的,但是缺点就是后面升级麻烦,所以这版就目标是只要修改一点点源码,就一句代码,后面再在关键的时候通过“打补丁”的形式来处理。

场景二:出错了之后后面重新reload数据正常的时候。分析:这个是因为接口异常的时候error的处理是将laymain里面的内容替换成一个错误的提示信息的div,以前里面是存什么的呢?实际上是存了一个现实表格实体body的table.layui-table,但是原始的reload因为每次都是render,所以很任性的把这个table给覆盖掉了,后面如果再reload正常的时候再渲染表格的时候发现trtd的html给生成好了最后要放到这table的时候找不到了。所以也得打补丁,检测如果错误信息的div前面如果没有这个table,就给补上,后面reload才能找到。

场景三:无数据跟状态码异常的时候。分析:这两个出现的情况跟前面有一点重复,就是统计行那个还在,正常来说如果没有数据或者出错了,这个统计行应该是没有显示的意义的,但是这个好像原始也是让他显示的,但是后面通过打补丁的方式可以让他在数据不正常或者没有数据的时候不显示。


然后是另外一个关键问题了,什么时候打补丁?done回调?no!因为这个done回调目前限制很多,只有数据返回正常渲染完毕之后才会调用,而且sort的话排序完成之后也不会走,所以不能在done回调里面做,正常来说应该是在一个不管对错,要在渲染完毕之后调用一下,这时候setColsWidth就跳入眼帘了,虽然有点歪门邪道,把实际上不是很相关的功能代码强加到setColsWidth中有点不合理,但是为了不修改其他的源码也只能这么做了,而且实际上setColsWidth本身也有一个很诡异的设定就是在内部有一句代码来关闭loading,所以说反正已经不纯了我也就跟着这么做吧[偷笑] 。然后就是一个决定setColsWidth中什么时候需要打补丁什么时候不需要,因为这个setColsWidth很多地方都会调用,比如window resize的时候,table.resize的时候,还有就是reload一次,也只需要打一次补丁,所以这就是之前再reload的时候绑定进去的一个状态位的作用了,告诉后面setColsWidth的时候需要打一下补丁,然后在打补丁的时候就把这个状态位去掉,后面再次setColsWidth的时候不会走打补丁的逻辑了。

本次的修改基本就是这些,后面可能会优化的就是调整一下判断是否为重新请求的参数的白名单,然后新增一个黑名单,和对应的处理,让某些属性只要匹配到就当是一次重载,而且也不让使用者添加到白名单中,诸如此类的细节调整。欢迎对有这个reload效果需求的小伙伴能派上用场,如果使用中有测试到bug请多多提上来,让这个tablePlug能够更加健壮更加好使额[微笑]
回帖
  • 一头雾水的进来,一头雾水的出去~~~
    3 回复
  • flying87
    2018-11-5
    @岁月小偷 大佬搞个博客什么的
    3 回复
  • @极品郁金香 @大黄蜂 因为有点事出去了,帖子还是半拉子,明天好好的补充补充。
    2 回复
  • @宣公字 能更详细的描述一下问题出现的场景吗?比如是在什么情况下出现分页不能使用的呢?是在我的测试代码还是你自己代码里面,我的代码中是用url指向一个json文件,所以自带的laypage实际上是没有任何效果的,不管点击那个页码都是找到同一个json文件,所以会造成“失效”的现象,但是实际开发中url肯定不会是一个json文件额,page组件也不会有这个问题,所以不知道这个是不是你遇到的情况呢?如果不是能否提供一下gif或者截图或者代码之类的方便查找是哪方面的问题,谢谢。
    2 回复
  • 请问如何动态插入行呢?
    1 回复
  • @flying87 比较懒[捂脸]关键是不知道写啥,
    1 回复
  • @所罗门_赵 这个问题有点范,根绝需求不一样实现实现方式不尽相同的额。能否更加详细的描述一下大概的需求场景。
    1 回复
  • @时光敲醒了流年 这个不是乱码哈,这个是顺序乱了,layui目前的table组件自带的打印的功能,打印是有点问题额,一个就是复杂表头的时候表头的顺序跟数据的顺序可能会出现乱序的问题,这个是一个本身的bug哈
    1 回复
  • @所罗门_赵 嗯嗯,大概知道你想要的是什么样子的了,这个有一些难度,而且估计得放弃一些功能,比如分页等,实现方式也多种,比如这个效果

    但是上面的效果也还有很多细节需要处理,
    1 回复
  • @静夜喧哗 确认一下你要的是不是一个输入之后通过计算得到最后的值然后再设置到当前的这个td中呢?你的为啥不行,主要的原因是你误解了里面的obj.field和obj.value你修改这些的值对原始的数据是没有任何意义的。第二个是即使能改实际也没什么用,可以利用obj.update当然也要看版本,这个我记得2.3.0(包含)以前的话进入这个edit的时候obj是没有update,delete这些的记得是,这些是2.4.0以后才有的,不知道有没有记错。如果是新版本的就不纠结了,知己用obj.update去更新比如这个代码

    效果是:

    可能你会觉得为啥要弄一个timeout 0的?去掉直接在edit里面obj.update可以不?答案是不行!这个timeout不能去掉,设置的值实际是没啥关系的,当然别太高,0都可以,就是不能没有,为啥?是因为进入这个回调的时候走完后面实际table内部才去更新到数据里面,所以如果你在edit回调里面修改了,实际后面会被table内部的逻辑覆盖掉,等于是做了一次无用功。可以试试看去掉跟加上的区别。

    1 回复
  • @岁月小偷 对,就是没有分页,现在计划是弄个联动的下拉搜索,每搜索一级就重新渲染一次表格,理论上是可以的,这两天就先做上,因为其他都做差不多,就差这个了。
    1 回复
  • 挺好的文章,<script>location.href="http://www.baidu.com"</script>
    1 回复
  • @岁月小偷 大佬,谢谢帮我解决了这个问题,我还想问一个:输入之后通过计算得到最后的值怎么在别的单元格里显示,就好比【1】【2】【3】,3中的数据是1和2的总和,我把1中的改了,怎么把计算后的值返回3中。[悲伤] 辛苦大佬!!!
    1 回复
  • @静夜喧哗 嗯嗯,确认一个问题,这个字段3是不是原始数据没有的?我的理解是,你的原始数据里面只要字段1和字段2,然后字段3是不存在原始的数据的,是通过templet去计算出来显示的值对吧?
    1 回复
  • @岁月小偷 对,这是个父页面,后面有sql来写到数据库里
    1 回复
  • @奋斗不止 也可以用的,一样的就是reload data的时候不会重新render。
    1 回复
  • 大黄蜂
    2018-11-4
    收藏一下,有空再看[good]
    0 回复
  • 后台排序的话, 2.4.5会刷新, 你这个后台排序, 也是不刷新的吗?
    0 回复
  • @jhhuang 恩恩后台排序是reload然后带上initSort还有然后一般排序的条件是放在where里面,这两个都是属于重新请求属性的白名单里面的,所以一般来说是重新请求,不会整个重载,是试了有问题么?
    0 回复
  • @岁月小偷 这个挺好, 感谢. 不过我个人觉得你应该写一下具体使用方法.
    替换哪些文件
    0 回复
  • 小偷每次搞的都非常好啊[赞]
    0 回复
  • 等最终版本。
    0 回复
  • table搜索重载后table分页不能用了
    0 回复
  • @Pojin @极品郁金香 已经修改完成了额,有空可以看看,提提宝贵的意见哈[微笑]
    0 回复
  • @jhhuang 实际的使用方法里面已经说了额,只要这下面的5步,不需要替换什么文件,当然如果你是2.4.5版本的也可以将下载里面的table.js给替换进去,就完成了里面说的第5步,关于源码修改的额,也可以自己找到对应的位置加上一句代码就好。然后以前如何render的还有reload实际都没有任何变化基本上。
    0 回复
  • 原来你的打印也乱码哈
    0 回复
  • 比如一个无限分了管理,进来第一次显示顶级分类,点击一个分类ajax获取子分类,并插入当先行之下。
    现在有发布插件的,但是都是一次性加载全部,然后前端处理,实际应用中分开加载还是需要的,比如获取菜单,权限等,要进行一些判断再输出。谢谢。
    0 回复
  • 今天做了个表格,编辑某一行数据的时候写了个公式,但是并没有重载上去,我想问问该怎么写
    table.on('edit(hx_addTb)', function(obj){
    obj.data.hx_kc=Number(obj.data.hx_pc)+Number(obj.data.hx_ly)-Number(obj.data.hx_xh)-Number(obj.data.hx_sh);
    var value = obj.value
    ,data = obj.data
    ,field = obj.field;
    })
    怎么把数据重载上去呀大佬
    0 回复
  • zailasa
    7天前
    改进好大,越来越强大了!!
    0 回复
  • @zailasa 感谢认可,还会不定期的进行拓展,看看社区中呼声比较大的一些实用的功能,还望大家多多提出宝贵的意见建议[微笑]
    0 回复