前言

最近闰土大叔跟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);
                }
            }
        }
    }

}

通过遍历currentColumns来找出是否按某一列进行过排序,如果有的话,就按照当前排序状态对更新后的数据做一次排序操作。

后记

最近忙碌于公司项目无法抽身,在公众号留言提问的小伙伴们,大叔建议你们可以去我的前端圈里交流讨论,公众号下面的菜单里有加入的方式。另外,你问我问题,我可能不会,就算我会,可能我也没时间。所以,欢迎大家多多关注我的公众号吧!


想了解我的更多动态?欢迎关注我的公众号:闰土大叔,里面有更多内容等着你!

手把手教你用Vue造轮子(3):开发可排序的表格组件的更多相关文章

  1. 手把手教你用webpack3搭建react项目(开发环境和生产环境)(一)

    开发环境和生产环境整个配置源码在github上,源码地址:github-webpack-react 如果觉得有帮助,点个Star谢谢!! (一)是开发环境,(二)是生产环境. 一.首先创建packag ...

  2. 手把手教你吧Python应用到实际开发 不再空谈悟法☝☝☝

    手把手教你吧Python应用到实际开发 不再空谈悟法☝☝☝ 想用python做机器学习吗,是不是在为从哪开始挠头?这里我假定你是新手,这篇文章里咱们一起用Python完成第一个机器学习项目.我会手把手 ...

  3. 手把手教你吧Python应用到实际开发 不再空谈悟法✍✍✍

    手把手教你吧Python应用到实际开发 不再空谈悟法 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问 ...

  4. 手把手教你把Python应用到实际开发 不再空谈语法

    手把手教你把Python应用到实际开发 不再空谈语法 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问 ...

  5. 循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)

    在我们使用Vue+Element开发前端的时候,往往涉及到很多界面组件的使用,其中很多直接采用Element官方的案例即可,有些则是在这个基础上封装更好利用.更少代码的组件:另外有些则是直接采用第三方 ...

  6. 手把手教你使用VUE+SpringMVC+Spring+Mybatis+Maven构建属于你自己的电商系统之vue后台前端框架搭建——猿实战01

            猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,跟着教程走下来,变身猿人找到工作不是 ...

  7. 从0开始,手把手教你用Vue开发一个答题App

    项目演示 项目演示 项目源码 项目源码 教程说明 本教程适合对Vue基础知识有一点了解,但不懂得综合运用,还未曾使用Vue从头开发过一个小型App的读者.本教程不对所有的Vue知识点进行讲解,而是手把 ...

  8. 手把手教你封装 Vue 组件并使用 NPM 发布

    Vue 开发插件 我们可以先查看Vue的插件的开发规范 我们开发的之后期望的结果是支持 import.require 或者直接使用 script 标签的形式引入,就像这样: ps: 这里注意一下包的名 ...

  9. 手把手教你使用Vue/React/Angular三大框架开发Pagination分页组件

    DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师.官方网站:devui.designNg组件库:ng-devui(欢迎S ...

随机推荐

  1. 【转载】Android Studio 导入External Libraries

    转载: 世军  Android Studio 导入External Libraries http://www.cnblogs.com/shijunzhang/p/5625432.html 导入本地Li ...

  2. Lua 数组排序 table.sort的注意事项

    1. table中不能有nil table.sort是排序函数,它要求要排序的目标table的必须是从1到n连续的,即中间不能有nil. 2. 重写的比较函数,两个值相等时不能return true ...

  3. [LeetCode] 二叉树相关题目(不完全)

    最近在做LeetCode上面有关二叉树的题目,这篇博客仅用来记录这些题目的代码. 二叉树的题目,一般都是利用递归来解决的,因此这一类题目对理解递归很有帮助. 1.Symmetric Tree(http ...

  4. EntityFramework Core数据查询

    前言 本节我们再来讲讲EF Core,本节算是回归基础吧,当前项目EF Core还是处于1.1版本中,后续等待.net core等版本稳定了全部会更新到2.0版本中,到时再来更新相关文章分享给大家. ...

  5. iOS SVN出现的问题,在mac使用Cornerstone中无法提交提交失败处理。。。

    问题一: Description : An error occurred while contacting the repository. Suggestion : The server may be ...

  6. UVALive 4850 Installations

    题目大意:有若干个任务,每个任务耗时si,期限为di,同一时间只能做一个任务.对于一个任务,惩罚值为max(0,完成时间-期限).问怎么安排,使(最大惩罚值+次大惩罚值)最小,O(n^2). 如果没有 ...

  7. Thomas Hobbes: Leviathan

    Man is distinguished, not only by his reason, but by this singular passion from other animals, which ...

  8. CSS3 自定义动画(animation)

    除了在之前的文章中介绍过的 CSS3 的变形 (transformation) 和转换 (transition) 外,CSS3 还有一种自由度更大的自定义动画,开发者甚至可以使用变形(transfor ...

  9. Keep Mind Working

    想找一个这样的地方,可以让脑袋持续运转着.不会像游戏一样让人着迷,不会像有色电视一样让人想错地方,也不会像工作一样充满太多严密.就是让脑袋继续转着,适意地思考些什么. 之前会跑去游戏里,至少没有太污. ...

  10. linux编译php gd扩展

    1 安装gd的依赖包 yum -y install gd gd2 gd-devel gd2-devel zlib freetype 2 安装jpeg: wget http://www.ijg.org/ ...