5 Advanced Tips for Vue Performance
By: Alexey Kuznetsov
Tags: Vue 2, Vue 3, Vuex, Vuejs, Vue

Component Distribution

Component is the core feature of the entire vue ecosystem. Understanding component’s life cycle and update rules is essential for building an efficient application. Usually when we start working with vue, we use some common sense about how to distribute the code over the components. For example, if there’s a list of repeating entries, then probably it’s good to move each entry-related code to the separate component.

Besides of that, the component distribution provides a powerful mechanism giving the exact control of what the granularity of the update is. It’s a core performance-related feature. Let’s consider this terrible code:

That’s the demo: vue2, vue3. Try to rename, check or reorder items. Any update, targeted to one item only, causes any other item in the list to re-render. The reason is that <Item> component refers to the extendedItemsByIds object, and the last is rebuilt when any item any property changes.

Every vue component is a function that provides some virtual DOM and caches the result depending on it’s arguments. The argument list is determined on the dry-run stage and consists on props and some variables in $store. If the argument is an object that is rebuilt after update, caching does not work.

Our initial store structure is bad. We started using normalizr approach, separated item and isChecked prop, but we have not finished. Our extendedItems getter is not good as well — it copies item properties instead of just referring to the initial item object. Let’s fix that:

This code works correctly for renaming an item (check vue2, vue3 examples), but checking/unchecking item still causes every item to re-render. And, surprisingly, item reordering works in vue2 but not in vue3.

The last is a breaking change, it’s intended behavior, but it’s not documented. The reason is referring to the scope variable (id) in the event handlers @set-checked and @rename . In vue3 a scope variable in an event handler cannot be cached, which means that every update will create a new event function, which will cause the component to update. Luckily, it’s easy to fix, it’s enough to send already prepared event that contains scope variable (id in our case) by itself. Check this example4p3 for vue3.

Upd. 2020–02–25: One more way to fix rerendering caused by referring to a scope variable in event handler is to specify all the events the component can emit in the new vue3 property called emits . Our ItemWithRenameById2 component can emit 2 events — @set-checked and @rename — if we add both like emits: ['set-checked', 'rename'] , item reordering starts working correctly. Here’s the example4p3-with-emits for vue3.

The last thing to fix is isChecked computed — currently it refers to the entire $store.state.checkedIds array (it tries to find a value there). Checking an item changes that array, that why every <Item> has to repeat the search. It can be fixed by sending isChecked as a boolean prop to every <Item> component instead of searching for the value inside:

Here are the examples of the final correctly working solution for vue2 and vue3.