Element UI 2.X el-tree的一些实践思考

2,010 阅读2分钟

Element UI 里面的树组件 el-tree 在业务开发里面比较常见。同时也有很多令人迷惑的地方【坑】。在此文章中做一次简单的总结。常规的使用方法请参考官方文档

迷惑 (一). 懒加载的树,节点更新问题

  • 官方文档里面更新树或者节点的方法如下:

image.png

  • 这些方法在 懒加载树情况下,均没有问题。

  • 但是: lazy load 模式下append 或 remove 后不更新 ,我最近业务需求需要操作懒加载的tree,更新append后发现效果,也很郁闷。关于这个问题:可以参考这个文章。可以解决部分业务场景问题。

  • 我这边业务里面使用的更新树逻辑:每次数据更新,只更新引起数据改变的数据的父节点node,达到类似单条更新的效果【非真正意义的单条更新,因为业务需求 需要当前的 一片段 的 树的数据最新】

注释:单条数据【单个node】数据更新方法: 方法:通过getNode拿到节点,更新data即可:

 let needOpeaterNode = (this.$refs.taskTree as any).getNode(keyPointId)
 needOpeaterNode.data = XXX

版本一:每次更新需要先让更新数据节点模拟第一次加载清空,如果当前的 节点已展开,会出现 “先收起、后打开”的效果。卡顿感比较明显。

动画2.gif


  refreshNodeBy(keyPointId?: any) {
    // 记录需要打开的节点
    const nodeMap: any = (this.$refs.taskTree as any).store.nodesMap;
    let needOpenId: any = [];
    Object.values(nodeMap).forEach((ele: any) => {
      if (ele.expanded) {
        needOpenId.push(ele.data.keyPointId);
      }
    });
    this.expandedKeys = needOpenId;
    //开始更新树
    this.$nextTick(async () => {
      let node = (this.$refs.taskTree as any).getNode(keyPointId); // 通过节点id找到对应树节点对象
      if (node) {
        node.loaded = false;
        node.expand(); // 主动调用展开节点方法,重新查询该节点下的所有子节点
        return;
      } else {
        // 更新根节点
        let node: any = this.$refs.taskTree;
        let rootNode = node.root;
        //更改根节点 loaded 属性 为 FALSE,然后调用打开节点方法,触发数据查询
        rootNode.loaded = false;
        rootNode.expand(); // 主动调用展开节点方法,重新查询该节点下的所有子节点
      }
    });
  }

版本二:相对于版本1,只是修改了数据更新逻辑顺序,不再操作节点的打开状态,手动请求最新的数据后,利用源码里面提供的方法用最新数据渲染树。修改后。体验更流畅。

动画5.gif


  refreshNodeBy(keyPointId?: any) {
    // 记录需要打开的节点
    const nodeMap: any = (this.$refs.taskTree as any).store.nodesMap;
    let needOpenId: any = [];
    Object.values(nodeMap).forEach((ele: any) => {
      if (ele.expanded) {
        needOpenId.push(ele.data.keyPointId);
      }
    });
    this.expandedKeys = needOpenId;
    //开始更新树
    this.$nextTick(async () => {
      let needOpeaterNode = (this.$refs.taskTree as any).getNode(keyPointId); // 通过节点id找到对应树节点对象
      let isRefreshRoot = false;
      if (!needOpeaterNode) {
        // 更新根节点
        let node: any = this.$refs.taskTree;
        needOpeaterNode = node.root;
        isRefreshRoot = true;
      }
      //================= 主要修改代码================
      let childs = needOpeaterNode.childNodes;
      let newData = await this.getTaskList(
        isRefreshRoot ? undefined : needOpeaterNode
      );
      //清除原来tree node挂载的数据节点。否则会导致数据被“拼接”处理
      childs.splice(0, childs.length);
      //调用源码 tree node方法,根据数据生成子节点
      needOpeaterNode.doCreateChildren(newData);
      //================= 主要修改代码================
    });
  }

迷惑 (二). element-plus Tree V2 虚拟化树形控件解决大数据渲染问题,为什么不能将这个组件下发兼容 vue2?

  • 因为业务需求,我们需要做 树结构 的 一键展开一键收起 逻辑。用 懒加载树 或 非 懒加载树显然会存在比较大的性能问题。百度之后,发现element-plus提供了虚拟树组件。但是不支持vue2,没有向下兼容。

没办法,自己找思路吧:

1)思路一【懒加载树】
  • 将需求,修改为“打开下一级”。效果如下:

动画1.gif

部分demo代码:

  openNextLevel() {
    let needOpen: any = [];
    let allNode: any = this.$refs.taskTree.store.nodesMap;
    Object.keys(allNode).forEach((ele: any) => {
      if (!allNode[ele].isLeaf) {
        needOpen.push(ele);
      }
    });
    //TODO 正式代码需要去重
    this.openKeys = [...this.openKeys, ...needOpen];
  }

这种方案其实没有解决性能问题,只是一个比较大的性能开销分几次而已。治标不治本。

1)思路二【懒加载树】

暂无 ==》》 冥想中~~~