阅读 2104

基于vue的海量数据tree组件(20w+)

写这个组件的原因:

  1. elementUI 里的 tree 组件在数据量为 1k+ 的的时候卡顿。
  2. 没有找到基于 vue 的海量(20w+) tree 插件。

1. 为啥卡顿。

简单分析说下为啥卡顿(具体查看参考文章):

  1. elementUI 递归实现 tree,dom 量大。
  2. 海量的 data 会被依赖收集,内存开销大,会卡顿,甚至提示内存不足浏览器奔溃。

2. 实现方案

  1. tree 数据和 dom 结构的扁平化。
  2. 虚拟长列表控制 dom 渲染数量
  3. data 数据不被依赖收集。
  4. 多种数据结构加速响应(多点空间换时间)。

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. 运行截图

参考文章

zhuanlan.zhihu.com/p/55528376

源码

github: 源码