Akryum / vue-virtual-scroller

⚡️ Blazing fast scrolling for any amount of data
https://vue-virtual-scroller-demo.netlify.app
9.69k stars 915 forks source link

very slow scroll if checkbox is enabled #347

Open AymanElarian opened 4 years ago

AymanElarian commented 4 years ago

hello I have an problem , my list is more than 10 k if I scroll without active checkbox grid scroll fast , but if all checkbox is active , it became very laggy this code inside the template

<el-col :span="1">
                <input type="checkbox" :id="item.id" :value="item.id" v-model="activeDevices" />
              </el-col>
NikitaIT commented 4 years ago

@AymanElarian most likely it is a matter of activeDevices binding, if you want to achieve good performance in vue for such binding on lists, then do the Mobx trick with limited reactivity by wrapping the reactive value in a function.

Something like this I do with composition-api.

In the list component create "reactive data non-reactive getter" for each item: props: {getValue: (item) => searchResultItem(item, selectedSetIds)}

In the component of a list item: render() {const v = props.getValue(props.item); return <el-checkbox {... v} />}

Then, when updating the checkbox, the entire collection will not be updated, but only 1 element.

When linked to activeDevices, 1 checkbox will cause all checkboxes to be updated due to reactivity.

I don’t know how to do this without composition-api, but I think you will find a solution.

I hope I understood your problem correctly, I do not use this library yet, but the situation seemed familiar to me, good luck.

function searchResultItem(
    item: any,
    selectedSetIds: SelectionMediator<number, string>
  ) {
    const propsSearchResultItem = reactive<SearchResultItemState["props"]>({
      ...mapItemToSearchResult(item),
      checked: selectedSetIds.has(item.id)
    });
    function handle(isSelected: boolean) {
      propsSearchResultItem.checked = isSelected;
    }
    selectedSetIds.on(item.id, handle);
    return {
      value: {
        props: propsSearchResultItem,
        on: {
          change: (checked: boolean) => {
            selectedSetIds.change(item.id);
          }
        }
      }
    };
  }
import { IsSelectedTuple, itemTogglerFor } from "@/utils/itemTogglerFor";
import { reactive, watch } from "@vue/composition-api";

export type HandlerMap<TId> = Map<TId, (isSelected: boolean) => void>;
export class SelectionMediator<
  TAdditiveId,
  TId,
  TPosibleId = TAdditiveId | TId
> {
  protected readonly _selected: Set<TId>;
  protected readonly _handlers: HandlerMap<TId> = new Map();
  get selected(): ReadonlySet<TId> {
    return this._selected;
  }
  get handlers(): Readonly<HandlerMap<TId>> {
    return this._handlers;
  }
  protected readonly toggle: (x: TPosibleId) => IsSelectedTuple<TId> = (
    x: TPosibleId
  ) => itemTogglerFor<Set<TId>, TId>(this._selected)(this._constraint(x));
  constructor(private readonly _constraint: (x: TPosibleId) => TId) {
    this._selected = new Set<TId>([]);
  }
  public change(id: TPosibleId): IsSelectedTuple<TId> {
    return this.handle(this.toggle(id));
  }
  public has(id: TPosibleId): boolean {
    return this.selected.has(this._constraint(id));
  }
  public select(ids: TPosibleId[]): Array<IsSelectedTuple<TId>> {
    return ids.map(this.toggle).map(this.handle);
  }
  private handle = ([id, isSelected]: IsSelectedTuple<TId>): IsSelectedTuple<
    TId
  > => {
    const handler = this.handlers.get(id);
    handler && handler(isSelected);
    return [id, isSelected];
  };
  public on(id: TPosibleId, onChange: (isSelected: boolean) => void) {
    const item = this._constraint(id);
    this.handlers.set(item, onChange);
  }
}

p.s. For 200 items on the screen, checkboxes have visible switching delays of about 0.8 seconds. With this method, a delay of 0.5 seconds starts at 2000 elements, and this(0.5) is a delay due to the browser, and not due to not optimal reactivity.