arco-design-vue icon indicating copy to clipboard operation
arco-design-vue copied to clipboard

修正一处VList的性能优化

Open xiaosongmao123 opened this issue 3 years ago • 1 comments

Basic Info

  • Package Name And Version: @arco-design/[email protected]
  • Browser: chrome105.0.0.0
  • Reproduction Link: https://github.com/arco-design/arco-design-vue/blob/main/packages/web-vue/components/_components/virtual-list-v2/virtual-list-item.tsx

问题virtual-list-item.tsx中的setItemSize()导致性能问题

在做一个下载列表,需要频繁的刷新列表中的下载进度,此时发现了一点性能问题,跟踪调试后发现是setItemSize方法的逻辑问题

#virtual-list-item.tsx
setup(props, { slots }) {
    const key = getCurrentInstance()?.vnode.key as string | number;
    const itemRef = ref<HTMLElement | ComponentPublicInstance>();

    const setItemSize = () => {
      // @ts-ignore
      const ele = itemRef.value?.$el ?? (itemRef.value as HTMLElement);
      if (!props.hasItemSize(key) && ele?.offsetHeight) {
        props.setItemSize(key, ele.offsetHeight);
      }
    };

    onMounted(() => setItemSize());

    return () => {
      const child = getFirstComponent(slots.default?.());
      if (child) {
        return cloneVNode(
          child,
          {
            ref: itemRef,
          },
          true
        );
      }

      return null;
    };
  },
#use-size.ts
 const hasItemSize = (key: string | number) => {
    return sizeMap.has(key);
  };

当前逻辑是, 1.读取ele 2.读取key 3.hasItemSize --> 从useSize.sizeMap中读取height 4.如果读不到就读取ele的offsetHeight并保存到useSize.sizeMap中

但是,问题出在对固定高度的virtual-list-item,并没有优化

当List被设置为fixedSize=true estimatedSize=50 时,我们应该理解为 virtual-list-item 是固定的50的高度

对固定高度时(fixedSize),virtual-list-item.tsx可以进行优化

#use-size.ts
 const hasItemSize = (key: string | number) => {
    if (isFixed.value) return true  //增加此处 固定高度时直接返回true,减少当大量数据时的map查找计算
    return sizeMap.has(key);
  };
#virtual-list-item.tsx
setup(props, { slots }) {
   
    const itemRef = ref<HTMLElement | ComponentPublicInstance>();

    const setItemSize = () => {
      // @ts-ignore
     if (!props.hasItemSize(key)){  //增加此行 使得固定高度时不需要运行下面的代码 
          const ele = itemRef.value?.$el ?? (itemRef.value as HTMLElement); //移动此行 固定高度时不需要读取ele
          const key = getCurrentInstance()?.vnode.key as string | number; //移动此行 固定高度时不需要读取key
          if ( ele?.offsetHeight) {//修改此行
                props.setItemSize(key, ele.offsetHeight);//注:固定高度时也不需要保存ItemSize,减少大量数据时map的插入hash计算
          }
      }
    };

    onMounted(() => setItemSize());

    return () => {
      const child = getFirstComponent(slots.default?.());
      if (child) {
        return cloneVNode(
          child,
          {
            ref: itemRef,
          },
          true
        );
      }

      return null;
    };
  },

如上修改,为的是固定高度模式下,减少不必要的 1.读取ele 2.读取key 3.hasItemSize --> 从useSize.sizeMap中读取height 4.如果读不到就读取ele的offsetHeight并保存到useSize.sizeMap中

附性能截图,1000行数据需要 27 毫秒,不理想

setItemSize

xiaosongmao123 avatar Oct 02 '22 03:10 xiaosongmao123

@xiaosongmao123 目前在优化虚拟列表的性能问题,会在近期的版本中放出

flsion avatar Oct 14 '22 08:10 flsion