手把手教你用Vue造轮子(3):开发可排序的表格组件
前言
最近闰土大叔跟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):开发可排序的表格组件的更多相关文章
- 手把手教你用webpack3搭建react项目(开发环境和生产环境)(一)
开发环境和生产环境整个配置源码在github上,源码地址:github-webpack-react 如果觉得有帮助,点个Star谢谢!! (一)是开发环境,(二)是生产环境. 一.首先创建packag ...
- 手把手教你吧Python应用到实际开发 不再空谈悟法☝☝☝
手把手教你吧Python应用到实际开发 不再空谈悟法☝☝☝ 想用python做机器学习吗,是不是在为从哪开始挠头?这里我假定你是新手,这篇文章里咱们一起用Python完成第一个机器学习项目.我会手把手 ...
- 手把手教你吧Python应用到实际开发 不再空谈悟法✍✍✍
手把手教你吧Python应用到实际开发 不再空谈悟法 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问 ...
- 手把手教你把Python应用到实际开发 不再空谈语法
手把手教你把Python应用到实际开发 不再空谈语法 整个课程都看完了,这个课程的分享可以往下看,下面有链接,之前做java开发也做了一些年头,也分享下自己看这个视频的感受,单论单个知识点课程本身没问 ...
- 循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)
在我们使用Vue+Element开发前端的时候,往往涉及到很多界面组件的使用,其中很多直接采用Element官方的案例即可,有些则是在这个基础上封装更好利用.更少代码的组件:另外有些则是直接采用第三方 ...
- 手把手教你使用VUE+SpringMVC+Spring+Mybatis+Maven构建属于你自己的电商系统之vue后台前端框架搭建——猿实战01
猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,跟着教程走下来,变身猿人找到工作不是 ...
- 从0开始,手把手教你用Vue开发一个答题App
项目演示 项目演示 项目源码 项目源码 教程说明 本教程适合对Vue基础知识有一点了解,但不懂得综合运用,还未曾使用Vue从头开发过一个小型App的读者.本教程不对所有的Vue知识点进行讲解,而是手把 ...
- 手把手教你封装 Vue 组件并使用 NPM 发布
Vue 开发插件 我们可以先查看Vue的插件的开发规范 我们开发的之后期望的结果是支持 import.require 或者直接使用 script 标签的形式引入,就像这样: ps: 这里注意一下包的名 ...
- 手把手教你使用Vue/React/Angular三大框架开发Pagination分页组件
DevUI是一支兼具设计视角和工程视角的团队,服务于华为云DevCloud平台和华为内部数个中后台系统,服务于设计师和前端工程师.官方网站:devui.designNg组件库:ng-devui(欢迎S ...
随机推荐
- mybatis中#和$符号的区别(转)
mybatis做为一个轻量级ORM框架在许多项目中使用,因其简单的入门受到了广大开发者的热爱.在近期项目中再做一个相关的开发,碰到了#.$符号这样的问题,之前没怎么注意过,通过学习之后,有了点感悟,分 ...
- oracle如何连接别人的数据库,需要在本地添加一些配置
2.oracle如何连接别人的数据库,需要在本地添加一些配置 1.找到 listener.ora 文件,打开(一般在 C 文件夹) ORCL = (DESCRIPTION = (ADDRESS = ( ...
- www.netcraft.com查看站点服务器使用的是什么操作系统
查看站点服务器使用的是什么操作系统
- linux集群批量执行命令
因为工作需要,需要修改集群中机器的配置,一台一台的修改太浪费时间,就想能不能通过自动化脚本批量执行命令,尝试写一个,自己shell不熟悉,写的有点渣渣 if [ "$#" -ne ...
- 解决MAVEN项目因achetype加载太慢的问题
解决方案: 加载太慢由于achetype-catalog.xml文件的访问问题,导致了整个构建过程的缓慢,所以是否能够将文件保存到本地,成为一种解决思路.翻阅Maven官方文档可以找到,确实是可以的. ...
- Illustration of Git branching and merge
网上看到的描述Git工作流程的图片,有些出处忘了保存,仅供学习. 1. One Git Branching Model 出处: http://nvie.com/posts/a-successful-g ...
- 我搞zabbix的那两天(2)
摘要:前一篇(我搞zabbix的那两天(1))我介绍了Zabbix的安装部署以及遇到的问题,这一篇将介绍zabbix 使用及短信等告警实现!!! Zabbix主界面及汉化方法介绍 1.1 初始化主界面 ...
- mysql 基础指令/命令
1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root - ...
- 数组a[n]中存放1-n中的n-1个数,给出算法找出重复的那一个数
问题描述: 数组a[n]中存放1-n中的n-1个数,给出算法找出重复的那一个数. 算法一: 对数组a[n]进行冒泡排序,如果冒泡所得的最值和前一个最值相等,则该最值为重复的数. 分析: 该算法时间复杂 ...
- leetcode — word-break-ii
import java.util.*; /** * Source : https://oj.leetcode.com/problems/word-break-ii/ * * Given a strin ...