Subscribe on changes!

v-model is slow (hangs) when used on a big select[multiple] element

avatar
Jan 5th 2024

Vue version

3.4.5

Link to minimal reproduction

https://play.vuejs.org/#eNqNUk1v2zAM/SuCLt2Axg6W9eKlQbehh+2wDetuVQ+ZzcRq9QWJdlIY/u+jpCX1sqLoTSQfHx+fOPCPzhV9B7ziy1B76ZAFwM6thJHaWY9sYB42bGQbbzU7I+iZMMLU1oQIVVAjNOwygt7c3r0VZllmHmKgAEE7tUagiLFlxrN+pm0D6lLwA4HgTHcKpVMZSVjrUFpD2I31hJRMGvbuYj6fE7bq16qDmI3BAzzm52oY5Dguy9yaR5Z5RFJDYSP71c3foRUbhqcVYmOsxg0mqikM+JhkZejtQegdGyLlTjbYVmwxn7v9h5hoQW5brBhpTZkxeZI4+DnHQNZt5La4D9aQ64lD8NpqJxX470l6EJzEZSMEXytld19TDn0H54d83UL98Ez+PuxjTvAfHgL4HgQ/1nDtt4C5fH3zDfb0PhbpVzpF6BeKPyFY1UWNGfapMw3JnuCS2i/pdqTZ/grXewQTDktFoRE5JrzgdE+fX1j9Se6iWKQ+8pNczLc502t34mMu/EtCM7LYFtGFqizrxlAbXaDsfWEAS+N0eUWw0ncGpYZZY/XVonhfXNBJBJymCwh69tvbHTlLJJPFY3+y2888mAZ8tOV1Y0/apqNPSv+NP5oy/gHIRUlh

Steps to reproduce

  1. Create a select[multiple] element with a thousands of options (e.g. 25K options)
  2. Use v-model on it
  3. Try to select a big chunk of options (> 500)

What is expected?

Quick v-model update

What is actually happening?

A very noticeable hang

https://github.com/vuejs/core/assets/2431842/dd3a6f1a-81fe-42bd-9f73-19a1f685c276

System Info

No response

Any additional comments?

I'm not just testing performance, I'm actually using this in production, for reasons I won't get into. I should mention that managing the state manually (e.g. using @change) is fast.

avatar
Jan 6th 2024

To a degree, that's expected.

We need to set the selected prop on each affected <option> when the parent sets the value during the re-render that was triggered by the change event of the select. To do that, we have to loo over all option elements, and for each element, look up the option's value in the list of currently selected values passed through v-model.

This is essentially a controlled input, where the component always controls which options are selected.

Your (totally valid) workaround only uses the @change event, which 1) doesn't cause a re-render and 2) thereby skips the work of re-applying the selected attributes to all those options through this bad loop.

We might have some room for optimization here, but to a degree v-model will always be inherently be slower than an uncontrolled select due to the additional work it does.

avatar
Jan 8th 2024

using a Set would indeed be more performant, but it doesn't cover all use cases, unfortunately.

We currently use a looseEqual() function to compare values, because v-model select does support objects, and also supports inline-defined objects like this:

<option :value="{ size: 'small' }"></option>
<option :value="{ size: 'medium' }"></option>
<option :value="{ size: 'large' }"></option>

an implementation with Set could not support that, as these inline objects only match in shape, but not in identity.

We could, in theory, think about a modifier for some kine of "performance" (v-model.opimized) mode that would then use a Set and leave it to the developer to insure to define values in a way that selected values can be identified through identity comparison.

But then again: Should we optimize for selects with lists so large that they are unusable anyway?

Edit: Looking at our implementation again, there could be away to use a fast path with a Set automatically for primitive values ...

avatar
Jan 8th 2024