当前位置: 首页 > Web前端 > HTML

全栈视角下的分页处理

时间:2023-03-28 14:52:27 HTML

作者:京东物流杨攀分页是web应用开发中最常见的功能。在使用不同的框架和工具的过程中,发现初始行/页的定义不一样,刻意组织记录。从这个技术角度来看不同层的实现。以及不同语言实现的比较。文章会从正常的web结构分层的角度梳理一下不同层的处理。分为数据库分页、服务器分页、前端分页。这里以mysql为例组织数据库分页。我们常用的数据库如Oracle/SQLServer等,都支持类似的分页语法。没有给出具体的例子。先从数据库层排序,也从根上分析分页的最终目的。前后端的逻辑和适配都是为了拼接合适的SQL语句。①MySQLLIMIT语法:[LIMIT{[offset,]row_count}]LIMITrow_count等同于LIMIT0,row_count。起始行的偏移量为0(不是1)参考:MySQL::MySQL5.7参考手册::13.2.9SELECT语句服务器/后端分页后端分页,简单的说就是数据库的分页。对于mysql来说,就是上面offsetrow_count的计算过程。这里选取常用的框架组件,比较其实现细节。pagehelper是JavaOrm框架常用的开源分页插件mybatisspring-data-jdbc是Java框架常用的数据层组件①pagehelper/***计算起止行号偏移量*@seecom.github。pagehelper.Page#calculateStartAndEndRow*/privatevoidcalculateStartAndEndRow(){//pageNum页码,从1开始。pageNum<1,忽略计算。this.startRow=this.pageNum>0?(this.pageNum-1)*this.pageSize:0;this.endRow=this.startRow+this.pageSize*(this.pageNum>0?1:0);}/***计算总页数pages/pageCount。*在分配数据总数的同时,也计算了总页数。*可以与Math.ceil实现进行比较。*/publicvoidsetTotal(longtotal){if(pageSize>0){pages=(int)(total/pageSize+((total%pageSize==0)?0:1));}else{页面=0;}}SQL拼接实现:com.github.pagehelper.dialect.helper.MySqlDialect②spring-data-jdbc关键类:org.springframework.data.domain.Pageableorg.springframework.data.web.PageableDefault/***偏移量计算,区别于pagehelper,页码从0开始,默认为0*@seeorg.springframework.data.domain.AbstractPageRequest#getOffset*/publiclonggetOffset(){return(long)this.page*(long)this.size;}/**总页数的计算是用Math.ceil实现的。*@seeorg.springframework.data.domain.Page#getTotalPages()*/@OverridepublicintgetTotalPages(){返回getSize()==0?1:(int)Math.ceil((double)total/(double)getSize());}/***偏移计算,与pagehelper不同,页码从0开始。*@seeorg.springframework.data.jdbc.core.convert.SqlGenerator#applyPagination*/privateSelectBuilder.SelectOrderedapplyPagination(Pageablepageable,SelectBuilder.SelectOrderedselect){//在spring-data-relation,Limit抽象为SelectLimitOffsetSelectBuilder.SelectLimitOffset可限制=(SelectBuilder.SelectLimitOffset)选择;//要从开始读取前20行,请使用limitOffset(20,0)。要读取接下来的20个,请使用limitOffset(20,20)。SelectBuilder.SelectLimitOffsetlimitResult=limitable.limitOffset(pageable.getPageSize(),pageable.getOffset());return(SelectBuilder.SelectOrdered)limitResult;}spring-data-commons提供mvc层的页面参数处理器/***将{@linkorg.springframework.data.domain.Pageable}注入控制器方法时设置默认值的注解.**@seeorg.springframework.data.web.PageableDefault*/@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.PARAMETER)public@interfacePageableDefault{/***如果请求中没有定义相应的*参数,则注入的{@linkorg.springframework.data.domain.Pageable}应该获得默认大小(默认值为10)。*/intsize()默认10;/***如果请求中没有定义相应的*参数,则注入的{@linkorg.springframework.data.domain.Pageable}应该获取默认页码(默认为0)。*/intpage()default0;}MVC参数处理器:org.springframework.data.web.PageableHandlerMethodArgumentResolver前端分页前端展示层,从服务端渲染方案和纯前端脚本方案到看分页的最终页面渲染逻辑。这里选用Java常用的模板引擎thymeleaf,以及流行的前端框架element。-用户界面。从用法和组件源码的角度,阐明终端处理分页的常用方式。①thymeleaf-模板引擎Thymeleaf是一个现代服务器端Java模板引擎,适用于web和独立环境。

②element-ui前端框架//fromnode_modules\element-ui\packages\pagination\src\pagination.js//page-count总页数,可以设置total或page-count来实现显示页码的功能;computed:{internalPageCount(){if(typeofthis.total==='number'){//使用Math.ceil计算页数returnMath.max(1,Math.ceil(this.total/this.internalPageSize));}elseif(typeofthis.pageCount==='number'){returnMath.max(1,this.pageCount);}返回空值;}},/***开始页面计算。页码从1开始。*/getValidCurrentPage(value){value=parseInt(value,10);//从源码的实现可以看出,它是一个稳定而强大的开源框架,在容错和边界处理上严谨周到。consthavePageCount=typeofthis.internalPageCount==='number';让重置值;if(!havePageCount){if(isNaN(value)||value<1)resetValue=1;}else{//强制赋初值1if(value<1){resetValue=1;}elseif(value>this.internalPageCount){//数据越界,强制拉回PageCountresetValue=this.internalPageCount;}}if(resetValue===undefined&&isNaN(value)){resetValue=1;}elseif(resetValue===0){resetValue=1;}返回重置值===未定义?value:resetValue;},总结技术总是相关的,想法总是相似的,方案总是一样的。单独分析某项技术或原理,总是有界限和困惑。只有纵向拉伸,横向对比,才能对技术方案有深刻的理解。在实战应用中,可以灵活运用。分页的实现方案最终由数据库决定。对于很多数据库来说,它是由SQL语法规范构成的,由我们常用的各种组件或插件适配而成。通过纵向对比,我们可以看到不同技术层的职责和通用适配的实现过程,这对我们日常业务通用开发和不同业务的兼容有很大的借鉴意义。横向比较,比如前端展示层的实现思路,其实是很不一样的。如果使用thymeleaf,结构简单明了,但是交互响应会通过服务器。如果选择element-ui,分页只依赖表现层,服务端完全解构。在技??术选型中,可根据各自的优缺点进行适当的选择。