写这个组件的原因:
- elementUI 里的 tree 组件在数据量为 1k+ 的的时候卡顿。
- 没有找到基于 vue 的海量(20w+) tree 插件。
1. 为啥卡顿。
简单分析说下为啥卡顿(具体查看参考文章):
- elementUI 递归实现 tree,dom 量大。
- 海量的 data 会被依赖收集,内存开销大,会卡顿,甚至提示内存不足浏览器奔溃。
2. 实现方案
- tree 数据和 dom 结构的扁平化。
- 虚拟长列表控制 dom 渲染数量
- data 数据不被依赖收集。
- 多种数据结构加速响应(多点空间换时间)。
1. tree 数据和 dom 结构的扁平化、 虚拟长列表控制 dom 渲染数量
2. data 数据不被依赖收集、多种数据结构加速响
不通过 props 传递,而是通过函数传递。
// 父组件
<huge-tree ref="huge-tree"></huge-tree>
axios.get(`/static/json/${count}.json`).then(({ data }) => {
// 注意:这里的 data 也不要被依赖收集了,会导致卡顿。
this.$refs['huge-tree'].setData(data);
});
// 子组件
<script>
class BigData {
_data = []; // 海量数据 tree
list = []; // 扁平化的tree
filterList = []; // 根据关键词过滤后的list
listMap = {}; // this.big.list 对应的 map, 便于快速找到节点。
filterTree = []; // 根据关键词过滤后的tree
}
export default {
data() {
this.big = null;
return {
count: 1, // 用于视图更新, 由于没有依赖收集,通过count 手动更新computed。
keyword: '', // 关键词
isSearching: false, // 搜索中
itemHeigth: 27, // 每一项的高度
startIndex: 0, // 渲染的开始区间
endIndex: 70, // 渲染的结束区间
throttleSrcoll: '', // 节流
debounceInput: '',
isOnlyInCheckedSearch: false,
};
},
computed: {
// 过滤掉 hidden 节点
unHiddenList() {
return this.count ? this.big.filterList.filter(i => !i.isHidden) : [];
},
// 虚拟高度,与隐藏的数量有关
phantomHeight() {
return this.unHiddenList.length * this.itemHeigth;
},
renderList() {
return this.unHiddenList.slice(this.startIndex, this.endIndex);
},
},
created() {
this.big = new BigData();
// 滚动时的节流
this.throttleSrcoll = throttle(this.setRenderRange, 80);
// 输入过滤条件的防抖
this.debounceInput = debounce(this.init, 300);
},
methods: {
setData(data) {
this.big._data = data;
this.init('init');
},
//init: 1. 拉平tree,2. 组织list,3. 过滤,4. 展开,5. 选中, 6. 回到顶部
init(op) {
// op: init, restore, showCheckedOnly
if (this.big._data.length === 0) return;
if (op === 'init') {
this.flatTree(this.big._data);
this.big.list.forEach(node => (this.big.listMap[node.id] = node));
}
this.initFilter(op);
if (op === 'init' || op === 'restore') this.initExpand();
this.setCheckedKeys(this.big.checkedKeys);
this.backToTop();
},
//......
}
}
</script>
3. 运行截图
4. 使用
npm i huge-tree --save 或 yarn add huge-tree -D
// demo.vue,
<template>
<div>
<btm-huge-tree></btm-huge-tree>
</div>
</template>
<script>
import { HugeTree } from 'huge-tree'
export default {
components: {
'btm-huge-tree': HugeTree
}
}
</script>
5. 使用文档
参考文章
源码
github: github.com/bitmain-fro…