之所以会造成卡顿,是因为该组件是一次性将所有数据全部渲染,dom数量过于庞大,并且在展开树操作的时候,用了大量递归循环语句,性能受到严重影响,造成卡顿。
我想到的解决方式有两种:
一:分页,让后端添加分页查询,以节点数统计数据条数进行展示。(但实际需求中较少,一般树形表格都是不分页)。
二:将请求到的树形数组数据备份(tableDataCopy),初始化仅仅展示指定层级(如第一层树)的数据,将第一层下面的所有的children全部置为null,并记录存在children的节点,设置hasChild的状态,判断是否存在子节点。这样就不会在初始化页面时展示完所有数据,实现按需加载。
以上仅仅能满足展示功能,当存在操作节点上移下移/删除/编辑等操作的情况下,展示的节点就不能同步刷新(因为备份数据不能双向数据绑定了)。处理办法:更新节点(两种方式,第一种使用render()来实现,第二种是给表格绑定key(利用diff算法),只要操作了数据,就改变key就能实现节点更新)。
请求数据和展开代码如下:
// 获取树形列表
getList(active) {
this.loading = true;
let data = Object.assign({}, this.searchForm);
API.getDepartmentListAll(data)
.then((res) => {
// this.tableData = res.data;
this.expandRow.push(res.data[0].id); //默认展开
this.tableDataCopy = res.data || [] // 备份的全量数据
if (active) { //是否是操作按钮点击的数据
this.tableData = this.commonJs.mapNewTableData(res.data,this.expandRow); //展开已展开的数据
} else {
this.tableData = this.commonJs.mapNewTableData(res.data,res.data[0].id); //默认展开第一层
}
this.componentKey += 1; //销毁更新dom
}).finally(() => {
this.loading = false;
this.getTableHeight();
})
},
// 展开按钮
getChildrens(tree, treeNode, resolve) {
let data = Object.assign({}, this.searchForm, {
deptId: tree.id
});
API.getDepartmentListAll(data) //传入展开按钮层级节点的id,获取子节点
.then((res) => {
this.tableDatas = JSON.parse(JSON.stringify(res.data.children)).map(item => { // 展示数据
// hasChildren 表示需要展示一个箭头图标
item.hasChildren = item.children && item.children.length > 0
// 只展示一层也可以配置tree-props里面的children为其他字段
item.children = null
// 记住层级关系
item.idList = [item.id]
return item
})
resolve(this.tableDatas)
})
},
当存在操作按钮,数据更新后,利用上述key更新节点后 ,会将展开的状态初始为初次进度页面的状态,不能停留在操作的节点。解决方法:可以在点击操作按钮的时候,使用findParentNode方法递归查询到该节点所有的祖先节点id并记录(expandRow,该变量也是树形表格:expand-row-keys默认展开的数据),然后再请求刷新数据时,通过mapNewTableData()方法递归处理将对应的祖先节点id对应的children给展示处理,其他的节点还是一样的置为null。代码如下:
/**
*
* @param 递归,当前节点的祖先节点id数组(包含自身)
* acceptUnitNodes树状结构数据,ids当前节点的id
*/
findParentNode(acceptUnitNodes, ids) {
var parentNodes = [] // 所有父节点
var forfun = function(id, nodes) {
for (var i = 0; i < nodes.length; i++) {
var currentNode = nodes[i]
if (currentNode.id === id) {
return currentNode.id
} else if (currentNode.children) {
var validNodeId = forfun(id, currentNode.children)
if (validNodeId && parentNodes.indexOf(validNodeId) < 0) {
parentNodes.push(validNodeId)
}
if (validNodeId) {
return currentNode.id
}
}
}
}
var validNodeId = forfun(ids, acceptUnitNodes)
if (validNodeId && parentNodes.indexOf(validNodeId) < 0) {
parentNodes.push(validNodeId)
}
return parentNodes
},
//递归处理数据,当有展开记录的时候,将有展开记录的数据的子节点显示出来
mapNewTableData(data, pidArr) {
let dataNew = JSON.parse(JSON.stringify(data));
function mapNewArr(dataNew, pidArr) {
dataNew.map(item => {
if (pidArr.indexOf(item.id) === -1) {
item.idList = [item.id]
item.hasChildren = true
item.children = null
// 记住层级关系
return item;
} else {
mapNewArr(item.children, pidArr)
}
})
return dataNew;
}
mapNewArr(dataNew, pidArr);
return mapNewArr(dataNew, pidArr);;
},
操作按钮:
//上移//下移
upDownLayer(index, row, type) {
this.expandRow = (this.commonJs.findParentNode(this.tableDataCopy, row.id)).splice(1); //调工具,获取点击当前节点的祖先节点id数组。
let loading = Loading.service({
target: document.querySelector(".tableBox"),
});
API.getShiftOrd({})
.then((res) => {
this.$message.success(type === 'up' ? '上移成功' : '下移成功');
this.getList(true);
}).finally(() => {
loading.close();
})
},