前言
最近闰土大叔跟Vue干上了,没办法,公司业务驱动,不用Vue没招啊,leader尝到了前后端分离带来的好处,除非你离职,哈哈哈,当然,那是不可能的,对于我这种要攒钱买房子的人来说。那还说什么呢,干就完了。今天,大叔将带你们手把手地造个轮子——开发一个可以对表格某一列数据进行排序的表格组件。
接下来,正文从这开始~
俗话说的好,写个功能组件还不让看展示效果的,都是耍流氓。直接上图:
看到动图是不是更形象生动了呢,之前一直羡慕别人文章里的效果动图,如今大叔也学会如何生成gif动态图了,想取经的童鞋可以私聊我。
先来简单的介绍下,一个标准的表格是由<table>、<thead>、<tbody>、<tr>、<th>、<td>等元素组成的。搁平常table布局早已经被我们前端仔摒弃了,没有语义化的东西。但当你想用来展示大量结构化的数据时,table却是最好的选择。
接下来,进入正题。
表格组件的所有内容(表头和行数据)由两个prop构成:columns 和data。两者都是数组,columns用来描述每列的信息,并渲染在表头<thead>里面,可以指定某一列是否需要排序;data是每一行的数据,由columns决定每一行里各列的顺序。
按照惯例,先初始化文件。
<div id="app" v-cloak> <v-table></v-table> <button>添加数据</button></div><script src="js/vue.js"></script><script>Vue.component('vTable',{ props:{ columns:{ type:Array, default:[] }, data:{ type:Array, default:[] } }}
var app = new Vue({
el:'#app'
})</script>
为了让排序后的columns和data不影响原始数据,我们需要给v-table组件的data选项添加两个对应的数据。
Vue.component('vTable',{ // ... data:function(){ return { currentColumns:[], currentData:[] } }}
columns的每一项都是一个对象,对象中有三个字段,分别是title、key、sortable。其中title和key字段是必填的,title用来标识这列的表头标题,key是对应的data中列内容的字段名。sortable是选填字段,如果值为true,说明该列需要排序。
var app = new Vue({el:'#app',data:{ columns:[ { title:'姓名', key:'name' }, { title:'年龄', key:'age', sortable:true }, { title:'出生日期', key:'birthday', sortable:true }, { title:'地址', key:'address' } ], data:[ { name:'司徒正美', age:26, birthday:'1999-02-21', address:'北京市朝阳区芍药居' }, { name:'吕大豹', age:30, birthday:'1992-01-23', address:'北京市海淀区西二旗' }, { name:'阮一峰', age:25, birthday:'1987-11-10', address:'上海市浦东新区世纪大道' }, { name:'张鑫旭', age:18, birthday:'1991-10-10', address:'深圳市南山区深南大道' } ]}})
然后在html结构里,把数据传递给组件v-table:
<v-table :data="data" :columns="columns"></v-table>
v-table组件目前的prop:columns和data的数据已经从父级获取到,但是v-table不直接操作它们,而是使用data选项的currentColumns和currentData。所以在v-table初始化时,需要把columns和data赋值给currentColumns和currentData,并且在mounted钩子函数内调用:
Vue.component('vTable',{ // ... methods:function(){ return { makeColumns:function(){ this.currentColumns = this.columns.map(function(col, index){ // 添加一个字段标识当前列排序的状态 col._sortType = 'normal'; // 添加一个字段标识当前列在数组中的索引 col._index = index; return col; }) }, makeData:function(){ this.currentData = this.data.map(function(row, index){ // 添加一个字段标识当前行在数组中的索引 row._index = index; return row; }) } } }, mounted(){ // v-table初始化时调用 this.makeColumns(); this.makeData(); }}
在上面的业务代码中,我们用到了map()方法,这是javascript数组中的一个方法,根据传入的函数重新构造一个新数组。排序分为升序(asc)和降序(desc)两种,而且同时只能对一列数据进行排序。为了标识当前列的排序状态,在map列添加数据时,默认给每列都添加一个_sortType字段,赋值为normal,表示默认的排序,也就是不排序。在排序后,currentData每项的顺序都有可能发生变化,所以给currentColumns和currentData的每个数据都添加_index字段,代表当前数据在原始数据中的索引。
到这儿,数据整理阶段便告一段落,接下来就可以用render函数来构造虚拟DOM了。
Vue.component('vTable',{ // ... render:function(h){ var _this = this; var ths = []; this.currentColumns.forEach(function(col, index){ // console.log(col.title); if(col.sortable){ ths.push(h('th',[ h('span',col.title), //升序 h('a',{ class:{ on:col._sortType === 'asc' }, on:{ click:function(){ _this.handleSortByAsc(index) } } },'↑'), //降序 h('a',{ class:{ on:col._sortType === 'desc' }, on:{ click:function(){ _this.handleSortByDesc(index) } } },'↓') ])); }else{ ths.push(h('th',col.title)); } }); var trs = []; this.currentData.forEach(function(row){ var tds = []; _this.currentColumns.forEach(function(cell){ tds.push(h('td',row[cell.key])); }); trs.push(h('tr',tds)); }); return h('table',[ h('thead',[ h('tr',ths) ]), h('tbody',trs) ]); }}
上面的h就是createElement,只是换了一个别名而已。表格主体trs是一个二维数组,数据由currentColumns和currentData 组成,然后先遍历所有的行,在每一行内再遍历各列,最终组合出主体内容节点trs。表头的节点ths要相对复杂点,因为加了排序的功能。
如果col.sortable没有定义 ,或者值为false,就直接把col.title渲染出来,否则除了渲染title,还加了两个<a>元素来实现升序和降序的操作。接下来,我们在v-table的methods选项里再添加两个方法(升序 / 降序)
Vue.component('vTable',{// ...methods:{ handleSortByAsc:function(index){ var key = this.currentColumns[index].key; this.currentColumns.forEach(function(col){ col._sortType = 'normal'; }); this.currentColumns[index]._sortType = 'asc'; this.currentData.sort(function(a,b){ return a[key] > b[key] ? 1 : -1; }) }, handleSortByDesc:function(index){ var key = this.currentColumns[index].key; this.currentColumns.forEach(function(col){ col._sortType = 'normal'; }); this.currentColumns[index]._sortType = 'desc'; this.currentData.sort(function(a,b){ return a[key] < b[key] ? 1 : -1; }) }}}
不管是升序还是降序,目的都是改变currentColumns数组每项的顺序。排序用的是JavaScript数组的sort()方法,这里之所以返回1和-1,而不是直接返回a[key]<b[key],也就是true或false,是因为在部分浏览器(比如Safari)对sort()的处理不同,而1和-1可以做到兼容。排序的思路是这样的,先将所有列的排序状态都重置为normal,然后设置当前列的排序状态(asc 或 desc),对应到render里<a>元素的class名称on。
当渲染完表格后,如果我们点击添加数据按钮(这里也可以设置为删除),使得父级修改了data数据,v-table的currentData也应该得到更新,如果某一列已经排序过,更新后应该监听处理一次排序。
Vue.component('vTable',{// ...watch:{ data:function(){ this.makeData(); var sortedColumn = this.currentColumns.filter(function(col){ return col._sortType !== 'normal'; }); if(sortedColumn.length > 0){ if(sortedColumn[0]._sortType === 'asc'){ this.handleSortByAsc(sortedColumn[0]._index); }else{ this.handleSortByDesc(sortedColumn[0]._index); }注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。