前言
大家好,我是小欲望。上周同事问我数组变化了为什么el-tabel的header顺序还是老样子。
问题
直接上代码:
<template>
<div>
<el-table>
<el-table-column
v-for="item in arr"
:key="item.key"
:label="item.label"
></el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
arr: [
{
key: 1,
label: '测试1',
},
{
key: 2,
label: '测试2',
},
{
key: 3,
label: '测试3',
},
{
key: 4,
label: '测试4',
},
],
};
},
mounted() {
setTimeout(() => {
this.arr.reverse()
console.log(this.arr);
},3000)
},
};
</script>
看结果
当我转换了arr数组但是未发生header的转变。
启动elememt调试
第一步
先下载elementui
有时候我们拉去项目会网络请求超时。一个小技巧。
github.com/ElemeFE/ele…
改成 git://github.com/ElemeFE/element.git 会提升下载速度。
第二步
直接执行npm run dev 它包含了安装所需要的包
当看到这个就知道运行起来了。
重要的事情说三遍
执行 http://localhost:8085/
执行 http://localhost:8085/
执行 http://localhost:8085/
第三步
这就是打开的页面了。
因为我是中文的,所以组件的例子代码就在这里面,是使用md格式写的。
寻找原因
这里就不说源码的对应的部分了。简单实现一个原因出来。
先看下目录结构,table是文件,store跟vuex差不多管理全局的数据。其他一一对应。
table
模仿tabel的自身
<template>
<div>
<Column v-for="item in arr" :key="item.key" :label="item" :store="store"></Column>
<Header :store="store"></Header>
</div>
</template>
<script>
import Column from './column'
import Header from './header'
import Store from './store'
export default {
components: {
Column,
Header
},
data() {
// 创建一个全局状态管理store每个组件都可以使用
this.store = new Store()
return {
arr: [
{
key: 1,
label: '测试1',
},
{
key: 2,
label: '测试2',
},
{
key: 3,
label: '测试3',
},
{
key: 4,
label: '测试4',
},
],
tableData: [],
};
},
mounted() {
setTimeout(() => {
this.arr.reverse()
console.log('this.arr', this.arr);
}, 3000);
},
};
</script>
column
export default {
render(h) {
h('div')
},
props: {
column: {
type: Object
},
store: {}
},
mounted() {
// 把column储存起来
this.store.commit('SET_ARR', this.column)
},
destroyed() {
// 在全局中删除掉这些数据
this.store.commit('SET_REMOVE', this.column)
},
}
这就跟el-table-column,header的title就是这里的column下的label标签。组件mounted是执行commit(SET_ARR),把数据放到store.arr中去。当destroyed就删除store.arr对应的数据。
store/index
import Watcher from './watcher';
Watcher.prototype.mutations = {
SET_ARR(state, name) {
state.arr.push(name)
},
SET_REMOVE(state, name) {
state.arr.splice(state.arr.indexOf(name), 1)
},
}
// 给store的原型添加commit方法代理到原型的mutations的某个属性
Watcher.prototype.commit = function(name, ...args) {
const mutations = this.mutations;
if (mutations[name]) {
//[this.states].concat(args) 这是因为apply的第二个参数是数组
mutations[name].apply(this, [this.states].concat(args));
} else {
throw new Error(`Action not found: ${name}`);
}
};
export default Watcher;
这个就跟vuex相似,绑定起来走commit就是执行mutations的某个属性函数。
watcher
import Vue from 'vue';
export default Vue.extend({
data() {
return {
states: {
// 3.0 版本后要求必须设置该属性
arr: [],
},
};
},
});
这个就是store,table所有的全局属性数据都存放在这里。这里就放一个arr。
header
export default {
render(h) {
return h('div', this.arr1.map(item => h('p', item.label)))
},
props: {
store: {}
},
computed: {
arr1() {
return this.store.states.arr
}
}
}
头部渲染,从全局属性store.arr数据中拿到column存放的数据,就拿到了label数据循环渲染了出来。
原因
从这个代码中我们就知道header从store.arr拿值渲染
,当column组件mounted往store.arr放数据,因为组件的销毁是与key有关
(diff算法),因此顺序虽然变化,但是column是没有走destroyed,因此store.arr顺序就没有变化。
解决方案
因为组件是根据key来判断是否需要复用。所以我们只需要把key更换成不一样
(例如添加一个Math.random())。所以我们只需要把key变成不一样就可以走column的destroyed,然后再mounted就重置了store.arr。这里就重新绘制了header,顺序就出来了。
最后
之前需要elementui的源码是有下载下来,但是一直都没有去看过,由于这次的原因,知道了看的流程与调试。还有就是源码中很多都是使用jsx和render函数来编写的,函数组件知识我还是比较欠缺。最后,希望文章对你有帮助。