一、先说需求
我想仿照elementUi中的table组件进行一次仿写练习,实现的功能不多,就是实现一下基础的展示和根据其中一列进行升序和降序的排列!!!
基本就是如此,图中我将升序、降序的参考列设置为了时间列,点击时间那个label,就会进行升序和降序的排列。
二、思路
<div class="flex">
<VTable :data="data">
<TableCol prop="date" label="时间" sortable />
<TableCol prop="name" label="名字" />
<TableCol prop="address" label="地址" />
<VTable/>
</div>
我们都知道,在table组件中,我们直接给table组件传入data数据,table将根据用户设置的tableCol中的prop属性来将这一列展示出来,
比如,你的prop设置为date,那么tableCol应该在table去拿到传入的data数据(如下图)(data一般为数组),然后将数组中每一组数据中的date拿出来,放到一个对象里,并且加上对应的id(用于后续随着一列数据的顺序的改变,其他列也按照改变列的顺序进行排序),重新变为一个新数组,并展示。
const tableData = [
{
date: "2016-05-02",
name: "王小虎1",
address: "上海市普陀区金沙江路 1518 弄",
},
{
date: "2016-05-04",
name: "王小虎2",
address: "上海市普陀区金沙江路 1517 弄",
},
{
date: "2016-05-01",
name: "王小虎3",
address: "上海市普陀区金沙江路 1519 弄",
},
{
date: "2016-05-03",
name: "王小虎4",
address: "上海市普陀区金沙江路 1516 弄",
},
];
展示的基本的思路就是如此,这样每一个tableCol负责一组data中同样的prop的数据的展示。
1.当,要求排序的时候,因为每一个tableCol负责自己那一组prop下的数据,当date的数据按照升序排列时,由于在computed阶段为每一个数据加上了id,
computed: {
colDatas() {
console.log("computed")
return this.tableDataCom.map((item) => {
// 更好的方案从这里tableData获取新的数组
return { data: item[this.prop], id: item.id }; //获取id来完成后续的排序
});
},
},
注意,tableDateCom 是 table组件中data数据,进入到tableCol组件后,本文使用inject的方式植入,并且将其赋值到data中的属性,用于后续生命周期钩子中的加ID处理
2.所以当这一组数据的顺序进行改变后,记录各个id的新的序列(sortIds),然后通知table,并将新的id数组告知table,
changeSort() {
// 变化顺序并且更新
if (this.sorted) { // sorted是一个flag 用来标记是升序还是降序
// 升序
this.colDatas.sort((a, b) => {
return b.data[9] - a.data[9]; //为了方便一点,直接先写死了
});
} else {
// 降序
this.colDatas.sort((a, b) => {
return a.data[9] - b.data[9];
});
}
this.sortIds = this.colDatas.map((item) => {
return item.id;
});
this.sorted = !this.sorted;
// 新的index的顺序带过去
this.$parent.$emit("sortChange", this.sortIds); // this.$on绑定在父级,这里必须加parent
},
3.这时,table将再次告知各个tableCol组件,将各自管辖下prop数组按照已经排序的prop的新的id序列,进行排序,来完成统一排序。
父组件table进行on监视sortChange事件,让每一个子组件tableCol进行按照新的ids进行重排
根据传入的新的id序列,对自己接收的父组件table的data的镜像数据,进行重排,当tableDataCom改变之后,会重新调用computed中的colDatas,进而完成更新。
三、一些事后反思的点与思考
1.不好的地方
1.不好的地方,我觉得首先,由于自己没有深刻理解这个table组件的意义,后来看了视频,人家都是通过data数据,来确定几个label的那一行,也就是标题行,data数据不需要拆解,
而我是通过每一个label,将原本的数据进行了拆解,将同属于一个label或者prop的数据,进行合并,然后用flex-direction = column来完成了一列的展示,效率很低。
2.由于我一开始对于table的理解有误,我不得不在这条歪路上越走越远,导致我,computed,watch,生命周期函数,以及将inject获得的数据放到data里,这些操作,有很多都是可以减少的。
2.反思的点
1. 用index来标识原来的数组 来 搭配id 来识别就会出现很多问题!
// 1 3 0 2 + 3 2 1 0 + 2 0 3 1 + 1 3 0 2 用index 出现 问题
原来我使用了index来标识数组,结果就会出现一种,升序和降序进行修改后,进行数组没有一下子变成想要的升序列或者降序列而是,出现了上面的第2、4 两种ids。
我也有点说不清那种情况,反正就是以后不要用index来识别每一个数据的顺序
2.this.$on this.$emit
这个问题,我发现了两种模式,
一种是我们常常在父组件中把@xxx= “xxx” ,写到哪一个<son @xxx = "sss"/>
这个时候在子组件son中,直接写this.$emit()
on the other hand,就是直接在父组件中mounted函数中,写this.$on监听事件的,像上面的一样,这个时候子组件就需要写this.$parent.emit()
原因:我觉得应该是那种@xxx=“sss”,也是写到子组件实例中,在实例化中,绑定到了子组件上!!!
3.computed的执行问题
这个是本次最大的收获!!!
在beforecreate和created两个生命周期之间,Vue 进行了initComputed,也就是初始化了computed中的每一个属性,并且放到vm实例上,进行了响应式处理,但是!!!
但是computed中的每一个属性,并没有执行
1 在initComputed中,对每一个computed属性的函数设置为userDef,并且设置赋值给了getter,
2 放到watcher里,用于后续的update!!!
3 将每一个computed属性的key放到vm里,进行响应式处理
所以到此为止,computed中的每一属性(xxx)没有执行,但是created之后,可以调用this.xxx来先执行一遍。
!!!!important!!!!
而在beforemount和mounted之间,由于挂载vm到浏览器上时,由于初始化数据,这时候的computed中每一个属性,就会自动被执行了!!!
有鉴于此,我在tableCol中将获得table的数据后,在created阶段,这个时候,data数据已经有了tableDataCom 是 inject进来的tableData的镜像数据。
在这个时候我判断源数据是否有id,没有我就自己加上,用于后续的操作!!