写点什么

SAP UI 搜索分页技术

作者:Jerry Wang
  • 2021 年 11 月 19 日
  • 本文字数:4029 字

    阅读完需:约 13 分钟

SAP UI 搜索分页技术

搜索分页技术往往和另一个术语 Lazy Loading(懒加载)联系起来。今天由 Jerry 首先介绍 S/4HANA,CRM Fiori 和 S4CRM 应用里的 UI 搜索分页的实现原理。后半部分由 SAP 成都研究院菜园子小哥王聪向您介绍 Twitter 的懒加载实现。


关于王聪的背景介绍,您可以参考他的前一篇文章:SAP成都研究院非典型程序猿,菜园子小哥:当我用UI5诊断工具时我用些什么

S/4HANA Fiori 应用搜索分页实现原理

以 S/4HANA Product Master Fiori 应用为例,如果什么搜索条件都不指定,默认会返回 25 条数据,并且在 UI 上显示该系统总的 product 数量,在 Jerry 使用的系统里总共存在 140 个 product。



该搜索分页的实现归功于 OData 请求的参数,skip=0&top=25,意为从请求命中的第0条记录开始, 总共返回25条记录。而另一个参数inlinecount,其工作原理可以类比 ABAP Open SQL 的关键字 SELECT COUNT(*),用于统计数据库表的条目数。



一旦用鼠标滚轮向下移动页面至屏幕底部,会自动触发一个新的 OData 请求,参数为 $skip=25&top=25。 这样,从第 26 到第 50 个 product 也从数据库表中读取出来显示在 Fiori UI 上。



为什么分页的尺寸为 25?在 Fiori UI 列表实现文件 sap.m.ListBase.js 里,默认的分页尺寸(GrowingThreshold)为 20,这说明一定存在某个配置,或者在 Product Master 应用某处的 JavaScript 代码将这个分页尺寸从 20 改成了 25。



由于篇幅限制,Jerry 直接揭晓答案了。S/4HANA 里的 Fiori 应用都是使用 Smart Template 技术实现的,其列表区域的实现位于 SmartTable.fragment.xml 这个模板文件里,growingThreshold 指定为 25。因为从时间序列上来说 SmartTable.fragment.xml 后于 sap.m.ListBase.js 加载,所以对于分页尺寸的定义其优先级更高。



如果您想通过自己调试找到这个答案,锻炼自己的分析能力,可以参考我的调试过程:


How does UI5 AutoGrowing list(Lazy Load behavior) work


以及使用 Smart Template 开发 Fiori 应用的介绍:


Jerry的通过CDS view + Smart Template 开发Fiori应用的blog合集


再来看看搜索分页的后台处理。在 Netweaver 事务码 ST05 的数据库跟踪视图里,能清晰观察到这个分页效果:每次到数据库的查询只命中并返回 25 条记录,如下图三条高亮的跟踪记录所示。



从前台通过 UI5 库文件发送的带有top 参数的 OData 请求,被后台接收并维护于 io_query_options 参数中:




而最终在数据库查询层面的分页处理,是由 ABAP Open SQL 的关键字 OFFSET 实现的。



上图第 1674 行 @lv_offset 的值,是基于 UI 传入的top 计算而得。

CRM Fiori 应用搜索分页实现原理

CRM Fiori 应用的前台搜索分页实现原理和 S/4HANA Fiori 应用类似,只是分页尺寸变成了 20。



上图的top 参数同 S/4HANA 应用的行为相同,传递到后台并得到处理:




CRM Fiori 后台的搜索分页处理和 S/4HANA Fiori 的区别:并未使用 ABAP 的 OFFSET 关键字,而由应用开发人员自行实现。


(1) 首先将所有满足搜索条件的记录的 GUID 全部从数据库取出,从数据库层返回到 ABAP 层。在我这个测试系统里,总共有 21 条记录,全部返回到了 ABAP 层:



(2) 由应用开发人员根据top 值,将多余的记录丢弃掉,保证最后只返回 20 条记录给 UI。



至此 S/4HANA 和 CRM Fiori 应用的搜索分页原理介绍完毕。更多细节,请参考我的博客:


Search Paging implementation in S/4HANA and CRM Fiori application

S4CRM 应用的搜索分页实现原理

S4CRM,全称为 S/4HANA for Customer Management,UI 开发技术仍然采用 WebClient UI。和 S/4HANA 基于 UI5 的前端技术截然不同,WebClient UI 走的是服务器端渲染的 BSP 路线。严格意义上讲,WebClient UI 不存在数据库层面的搜索分页,其分页行为仅仅体现在服务器端渲染上。


下图例子里,我指定 Max Number of Results 为 200,意思是期望满足搜索条件的记录里,显示 200 条到 UI 上。



从搜索结果能看出分页效果。全部 200 条记录已经从数据库查询出来并保存到应用程序的内存里。如下图所示:



WebClient UI 仅仅将第一页的 HTML 源代码在 ABAP 后台渲染出来然后显示给用户。当用户点了屏幕下方的“2”页码时,不会有任何的数据库查询发生,服务器做的事情仅仅是将第二页对应的 HTML 源代码渲染出来。



服务器怎么知道应该渲染第二页的源代码呢?这个信息也是点了“2”页码后,从前台传给 ABAP 服务器的:



ABAP 后台拿到这个 visibleFirstRow 参数后,知道从搜索结果记录里的第 21 条开始渲染,一直到第 40 条。




更多渲染细节,请参考我的博客:


Paging Implementation in S/4HANA for Customer Management


了解了咱们 SAP 的搜索分页实现原理后,让我们再来看看其他厂商是怎么做的。


像国内的知乎,简书,新浪微博这些网站,其列表显示均实现了懒加载。菜园子小哥王聪对这些实现也很好奇。为什么最后选择了 Twitter 去研究?这就得从他和基友老金的故事说起。


下面是菜园子小哥王聪的讲解。还是老规矩,您可以点击文末的"阅读原文”,获取王聪的中英德三个版本的讲解文章。



懒加载,看看 Twitter 是怎么做的

老金痛恨 Twitter。


老金是我在德国读书时的好基友,在国内时就酷爱文学创作。但他却从未开通个博客什么的,坚持使用新浪“长微博”功能写文章。用他的话说,这代表新锐文学的姿态。到了德国之后,老金发现人家老外不用微博,人家用 Twitter。新锐的他自然要入乡随俗,可正准备舞文弄墨,却发现 Twitter 里并没有个东西叫“Long Twitter”,140 个字符啥也干不了。于是老金愤而卸载 Twitter,逢人便感慨西方文学这下是要彻底完了。


看着老金整天闷闷不乐,我便安慰他说什么长微博,不就是文字变图片嘛。Twitter 没这东西,看小爷我的本事啊。我给你写个 App,名字就叫“大 Twitter”,图标我都给你设计好了。



然后我用了两个晚上搞了个小工具,把大段文字转成图片,然后直接发到 Twitter 上。



可没想到,老金刚用了半天就找到我,说自己写的东西不知道为什么全被打上了马赛克,并信誓旦旦对“秦老师”发誓说自己没写什么大尺度的东西。我问他秦老师是谁?他说是印度著名诗人秦戈尔老师啊!善良的我并没有当面给他指出那位老师不姓秦这件事,只想着好好的图片怎么会被打码了呢?


我拿来一看,原来是老金实在憋了太久,这一次足足写了 8400 多个字,生成的图片尺寸过大,被鸡贼的 Twitter 给压缩了,于是便模糊得像打了码一样。心灰意冷的老金决定与 Twitter 恩断义绝,连账户都注销了。


虽然我也不怎么用 Twitter,但作为一个程序员我对它还是很有兴趣的。作为同类产品中的佼佼者,Twitter 自然是有它的优势。其中比较有特色的一点就是其懒加载的机制。今天我们就通过 Debug 的方式来对其探究一番。

一些你需要知道的概念

时间轴(Time Line): Twitter 中最最重要的部分。一条条的推文组合在一起,就成了页面上中间那条长长的时间轴。



位(Position): 一条推文的标识符,说白了就是推文的 ID。新推文的 Position 比老推文的要大,所以我觉得 Position 很有可能代表着“这是 Twitter 有史以来的第 xxx 条推文”。可我随便找到的一个 Position 却着实大得让我怀疑自己的猜测。


千里之行,始于 Network

首先我们在开发者工具的 Network 工具中截取一条当用户滚动加载时发出的请求。结果发现它长下面这个样子。




在这里我们可以发现几个有意义的信息:


  • max_position:翻遍 Header 信息以及请求参数,这是唯一一个跟所要请求的内容相关的东西。具体含义后面再讲。

  • has_more_items:顾名思义,服务器通过这个字段告诉前端是否还有更早的内容。

  • items_html:格式化之后发现,这个部分就是我们所请求到的推文内容。显然 Twitter 使用到的是后端渲染的技术,将推文内容渲染好直接发给前端进行展示。

  • min_position:恰好对应了请求当中的 max_position。

  • new_latent_count:这一次所请求到的推文的条数。

深入探究

为了搞清楚这些信息到底是怎么回事,我们通过寻找请求的发起者来深入到代码当中。原来 Twitter 在这里发送了一个 XMLHttpRequest。无论是什么请求,总归要有一个处理的方法,我们在 Call Stack 中层层向上追溯,然后找到了请求的定义位置。



这里我们进入到请求成功的方法中继续探索。最终到达终点,items_html 被添加到了时间轴当中。




那 min_position 和 max_position 呢?我们回到刚才定义请求的位置继续向上追溯,找到了 getOldItems 的方法。当用户在时间轴上向下滚动鼠标到最后时,就会调用到这个方法,而在其中会把上一次响应当中的 min_position 赋值给这一次请求当中的 max_postion。




至此我们可以将整个 Twitter 的懒加载流程串接起来:


  1. 用户向下滚动时间轴,发出请求,通知服务器“我已经把第 A 条看完啦,快让我看更之前的内容”。

  2. 服务器返回从 A 再往前的 20 条内容,并告诉用户“喏,现在发给你直到第 B 条的所有内容了,慢慢看吧”。

  3. 用户再次看完这些内容,向下滚动时间轴,告诉服务器“到第 B 条的我也看完啦,B 之前的你再发给我吧”。

每次不一定 20 条?

在研究的过程中,我发现了一个有趣的现象,就是 new_latent_count 绝大多数都是 20,而偶尔会略小于 20。由于前端请求中并不存在所要请求的条数,所以这个决策是在后端完成的。


起初我以为后端会根据需要即将响应的内容大小决定发多少条,可分析了一些例子之后发现有的时候响应明明很小,却还是发了不到 20 条。所以我的猜测是后端这个神奇的算法可能会判断返回的内容用户大概会浏览多久,如果比较耗时,则少返回一些。例如如果推文中有长视频,则判断为阅读耗时较长,可以少返回几条。但这只是我瞎猜的,有知道其中原理的朋友可以留言告诉我,非常感谢。

Debug 之痛

坦率讲整个 Debug 过程花费了我很多时间,一方面是对于其代码结构的不熟悉,另一方面是 minify 过的 js 代码实在是让人头疼啊。所有的变量都长成 abcd 不说,到处都是用逻辑运算符写的条件判断语句,看得人口吐白沫。


不过从学习的角度讲,整个过程跑下来无论是 debug 能力还是代码阅读能力都会有所提升,推荐大家也试一试。

更多阅读

发布于: 2 小时前阅读数: 6
用户头像

Jerry Wang

关注

个人微信公众号:汪子熙 2017.12.03 加入

SAP成都研究院开发专家,SAP社区导师,SAP中国技术大使。

评论

发布
暂无评论
SAP UI 搜索分页技术