Причина написания этого компонента:
- Компонент дерева в elementUI зависает, когда количество данных превышает 1k.
- Плагин массивного (20w+) дерева на основе vue не найден.
1. Почему застрял.
Кратко разберем, почему картон (Подробнее см. в справочной статье):
- elementUI рекурсивно реализует дерево с большим количеством dom.
- Большой объем данных будет собираться зависимостями, накладные расходы памяти будут высокими, он будет зависать, и даже браузер будет падать из-за нехватки памяти.
2. План реализации
- Сведение данных дерева и структуры дома.
- Виртуальный длинный список для контроля объема рендеринга dom
- data Данные не собираются зависимостями.
- Различные структуры данных ускоряют отклик (многоточечное пространство для времени).
1. Сведение древовидных данных и структуры домена, виртуальный длинный список для управления количеством рендеринга домена.
Подробнее см. в справочной статье
2. Данные данных не собираются по зависимостям, а множественные структуры данных ускоряют отклик
Не через пропсы, а через функции.
// 父组件
<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.com/bit main - поставь ох...