【分享吧】以列表滑动翻页实例思考在Vue框架下组件的封装思想

论坛 期权论坛 期权     
大连飞创   2019-1-21 17:51   4599   0


  Vue是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。
  相信对于前端的开发人员来说,关于Vue都会有大致的了解,或者对于组件封装也大致有部分自己的经验,在今天的文章中不会对于Vue的语法进行过多的阐述,而是站在组件封装的角度进行思考。
  说到组件,网络上有很多成型的开源组件库,而Element就是一套为开发者、设计师和产品经理准备的基于Vue 2.0的桌面端组件库,使用这个组件库中的元素来搭建界面将会节省很多时间。但是Element组件也有自身的限制,如果直接使用,则前端页面的代码量也比较繁冗,并不会根据业务页面的差异灵活配置,所以这就需要进一步考虑将子组件封装成父组件,从而让开发人员进行灵活的配置及调用。
一、封装组件需要具备的特性
1.使用简便性
  由于封装组件的目的是要用最简单的代码来构建前端的html,使开发人员的精力主要放在后台代码的逻辑实现与前台的交互上,所以封装组件的调用不易繁琐,越简单越好,最简方案就是使用一个html自定义标签来代表封装组件,比如,而所有的组件实现都在dfitc-table相应的组件文件里面实现。前端开发人员仅仅需要在页面中放置一个标签就可以将一个拥有繁重功能的列表或者批量输入的控件在前端进行引用,而无需为内部繁重的实现进行烦恼,增加了代码的可复用性和开发效率。
2.参数可控性
  前文说到,封装组件的目的是为了根据不同业务要求进行统一的组件样式管理,方便前端开发人员进行调用,所以组件的通用性同样伴随着业务差异性的冲击。所有页面都可以调用的通用组件如何做到根据不同业务进行不同的显示,这就涉及到组件的参数管理了。
  组件的参数传递一般都是为了满足业务页面的不同差异性,如数据显示 、样式控制、功能触发等。所有需要与组件进行交互的参数都需要从业务页面进行传递,而参数必须要有默认值,支持在不使用的情况下,组件会默认相应参数值。
  参数类型一般分为两类:数值和方法,而数值参数分为定量和变量。
  定量在组件标签中会通过如 type=”1” 的方式来进行配置,此配置在页面中不可更改,建议如宽度、高度、查询策略、翻页策略等这种不会在操作中动态变化的属性参数可以设置为定量。
  变量在组件标签中会通过如 :type=”value” 的方式来配置,而value的值在js代码中可进行动态定义及更改,当操作相关功能进行变量更改之后,组件会直接监听参数的变化进行页面的改变。
方法在组件标签中会通过如 @fuc=”fucvalue” 的方式来配置 ,而fucvalue需要在业务页面的methods中进行方法定义,来承载从组件中触发返回的相应方法。
3.可扩展性
  组件是可扩展的,封装后的组件随时可能因为新的业务模式而进行功能增加及方法优化,而这些优化都是在组件内部进行,前端调用最多配置几个参数或加一个方法。无论怎样扩展,都不应该影响已有业务页面调用组件的功能。所以组件的扩展需要极为小心,需要具有全局的考虑。
二、列表滑动翻页组件的实现与思考
  接下来我们来实际进行一个组件封装的操作,其中会涉及到各种Vue的结构知识。
1.组件的建立
  首先在Vue的工程中找寻一个合适的位置建立文件夹放置你的自定义组件,在文件夹中可以新建立一个.vue文件作为封装组件的文件,所有的组件代码及控制都在.vue文件中实现,例dfitcTable.vue。
  之后在同目录下建立index.js文件来统一管理组件,文件内容可如下设置。

  最后在外侧的main.js总体引入文件中,将此index.js引入进工程,就可以在程序的任何地方使用进行组件调用了。引入内容可如下设置。

2.组件的开发
  组件的开发遵循Vue的一般语法规范,如程序中引入了element-ui可直接使用组件库作为子组件进行搭建,也可以自行编写html代码。
  Element组件库再此就不冗述了,网上有很详细的组件实例演示及方法调用。
  而关于的内容编写则需要清楚export default{}里面各部分的作用与关联。
  props:设置业务页面向组件传输的数值参数,此参数名称需要与调用时属性名称一致,如:type=”1”或:type=”value”,则需命名为type,可设置类型及初始值,例如下图。

  data:设置封装组件中需要使用的变量,并可以对其做初始化定义。例如下图。

  methods:设置封装组件中内部调用的方法。如果需要向业务页面进行回调方法触发的话需要用以下方式进行调用,其中第一个参数是@后面的方法参数,其他的是传输参数。

  computed:设置计算变量,可以将多种变量动态拼接并赋值给相应属性。
  mounted:业务页面首次加载封装组件的时候调用的方法,用来初始化赋值和统一调用后台api。
  watch:用来监听业务页面数值参数的变化,并根据相应参数变化进行方法调用及组件变化控制。

  通过各部分的作用与关联可以实现参数传输与控制,进而实现组件内部的实现逻辑。而业务页面调用的时候直接以以下方式调用即可。

3.滑动翻页功能的实现思考
  滑动翻页功能及文章中所描述的所有组件封装技术均在“新一代柜台系统”实现,实现效果如下图所示。

  了解过组件建立与开发模式,接下来我们就可以实际动手来进行编码了。
  我们在实现组件的文件中可以灵活使用和组件以及它们所提供的方法。可以通过业务界面传输过来的数值参数来构建组件功能,也可以在业务界面通过传输json数据来动态构建每一列的数据显示及样式。当然我们可以活用Element组件提供的表头排序功能(sortable)及表头筛选功能(:filters和:filter-method)增加前端组件的可用性。具体如下所示。

  其中动态变量为:
  tableData:业务页面查询到的数据,以数组的方式传输进入组件,默认为[];
  tableHeight:列表的高度,可动态改变,默认为100;
  tableStyle:列表样式,可动态改变,默认为{width: ‘100%’};
  loadFlag:计算变量拼接id,用于向自定义指令传值;
  showIndexFlag:设置列表是否显示编号序列,默认true;
  tableColumn:以json的方式传入组件,进行循环渲染,默认为{},可如下设置;

  tableInfo:需要组件在统计区域显示的信息,如总数、总和等。
  其中调用方法为:
  tableLoadMore:向下滑动滚动条触发方法,可在方法中触发翻页功能;
  tableLoadBefore:向上滑动滚动条触发方法,可在方法中获取已查询缓存数据功能;
  returnRowClick:点击行获取行数据功能;
  transferValue:将需要转换的列通过此方法进行转换,包括数据字典代码转换、查询数据库获取键值对替换、日期格式转换等。
  接下来的方法实现与变量控制,就可以根据正常的翻页逻辑进行编码。
  在实现下拉翻页的时候遇到了一些需要重点实现的方案,在这里与大家分享一下:
  查询数据缓存实现模式:
  所有实现翻页的列表都会遇到在翻页期间数据有修改的情况,除非数据进行了一定时期的固化。开发人员无法预测用户在使用期间是否翻页,在翻页期间是否进行数据的增删改操作。所以下拉翻页就出现了两种实现模式:
  一种是点击查询按钮进行第一页数据查询,之后根据滑动滚动条进行下一页的数据查询,每次翻页操作都会查询数据库相应位置的数据。这种方式会减少前端与后端的交互,但是对于数据有修改的情况则会使临近两页的数据出现错乱的现象。
  另一种是点击查询按钮进行后台的循环调用,根据分页情况多次请求后台,将数据库数据批量缓存至后端或前端,请求后端数据不影响前端页面的同时操作。而翻页操作会读取已经存在缓存的数据,这样就可以保证翻页所查询的数据尽可能的都是在相同时间查询回来的。这种方式的弊端是在超过万级单位数据返回的时候,前端向后端交互比较频繁,而且可能一直无休止。虽然在重新查询和关闭页面会停止当前查询流,但是在查询期间的后台缓存数据是十分巨大的。
  大数据量页面翻页加载模式:
  前端页面之所以加入翻页功能,主要是缓解大数据的前端查询压力,如果每次下拉翻页而导致数据一直进行页面累加显示,页面上承载的数据会越来越多,在千级数据显示过后,浏览器基本上就会耗尽内存而变得十分卡顿。为此我们又考虑了另一种加载模式,上下翻页模式。
  上下翻页模式,顾名思义就是控制滚动条进行上下滑动进行翻页。向下翻页,如果缓存中存在下一页数据则从缓存获取数据,如果缓存中不存在下一页数据则从数据库获取下一页数据。向上翻页,如果不是第一页数据则可直接从缓存中获取上一页数据。而页面中永远只显示当前两页的数据,两页之前的数据与两页之后的数据全部不显示,而等待翻页才获取显示。
  这样的模式即使页面中存在以万级为单位的数据,页面显示也仅仅显示当前两页的数据,保证页面不会卡顿。
  但这样的模式也有一个限制,那就是Element自带的表头排序及筛选失去了部分意义,因为此排序筛选均为前台排序,而排序仅仅涉及到页面显示的数据,所以只会对当前显示的这两页进行动态排序和筛选,而如果进行全部数据排序又会导致页面数据跳跃,用户体验效果会比较差。
  翻页多选数据缓存模式:
  翻页列表面临的一个问题就是多选操作的控制。如何在选择数据之后,在进行上下翻页的时候继续缓存已选择数据?怎样在勾选或不勾选全选框的时候将包括没有显示的所有数据进行选择或清空?当然也要注意在数据全部选择之后,继续进行翻页操作的大数据量缓存对浏览器的性能影响。
  对此我们可以在组件中使用@selection-change和@select-all方法进行多选按钮的监听方法调用,在方法中通过内部变量记录当前选择数据、上一次选择数据、上一次选择时页码。通过页码的控制及选择数据的比较进行多选数据的缓存。
  通过this.$refs.filterTable.toggleRowSelection(row, true)进行单行选择状态更改。
  通过this.$refs.filterTable.clearSelection()进行全部选择状态清空。
  通过this.$emit(‘multipleSelection’, this.multipleTemp.select)进行业务页面选择方法的调用。
  需要注意的是this.$refs.filterTable.toggleRowSelection(row, true)方法只支持单行数据的状态变换,如果是多条数据,需要进行forEach循环调用,但是每次调用的时候都会进行@selection-change方法的调用。所以在编写代码的时候需要注意@selection-change方法里面的执行顺序,另外需要防止多次无意义的调用this.$emit(‘multipleSelection’,value)方法,如果在全选的时候循环调用此方法会因为系统交互过多导致浏览器卡死。
4.自定义指令的使用
  在组件的搭建过程中我们可以灵活使用自定义指令来为组件服务。
  以滑动翻页为例,在滚动条上下滑动进行事件触发的时候就使用了自定义指令功能,在组件实现中使用v-loadmore=”tableLoadMore”来调用自定义指令和设置返回调用方法,具体自定义指令实现如下图显示。

  通过以上方法,监听scroll滚动条的滑动距离,随之根据位置进行方法调用返回组件界面进行翻页方法触发。
  自定义指令的参数传递有两种方法,一般会用v-loadmare=”{a:xxx, b:xxx}”的方式。虽然在自定义指令中可以通过binding.value.a和binding.value.b进行数值的传递,但是仅仅能够获取第一次传进来的定量,如果xxx是一个变量则不能够通过binding.value.a的方式获取变化后的数值。所以我们可以动态获取对应元素的属性,实现方式可以是将变量动态传给元素的id,通过el.id进行获取,而且每次调用获取的都是最新的id属性值,这样我们就实现了变量传递。
  当然如果想在组件中直接实现控制页面滚动条位置的变化的话,则可以通过this.$refs.tablename.bodyWrapper.scrollTop = 10进行直接触发变化,不过别忘了在标签中增加ref=”tablename”的属性。
5.子组件样式覆盖
  关于子组件样式的覆盖,一定要注意不要污染其余的页面,除非想要把样式使用在全局的引用上面。
  页面中可以加入标签来进行样式定义,但是一定要注意和标签的不同用法。
  里面所设置的样式在程序打包之后会渲染至全局,所以尽量不要在里面增加私有组件的样式。
  里面所设置的样式只会在本页面进行使用。程序内部实现的方式是通过给html的dom节点加一个不重复的data属性(形如:data-v-f3f3eg9)来表示他的唯一性,而在每句css选择器的末尾(编译后的生成的css语句)加一个当前组件的data属性选择器(如[data-v- f3f3eg9])来私有化样式。这样就可以实现本页面的样式私有化了。
  但是scoped方式对于element-ui组件有一定的限制性,由于子组件是由多层组件嵌套的,那么如果我们想要私有化样式,对于当前组件下调用的其他组件,data属性只会添加到第一层html中,导致内部再次嵌套的组件无法使用私有化样式,如果出现此类情况,我们的解决办法是使用深度作用选择器,如下图所示。

  这样就可以将样式深度私有化渲染至更深层的组件了。
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3174
帖子:645
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP