2.3.0
https://codepen.io/anon/pen/YxXMXb?editors=1111
The store state has two values selectedItems and allCategories. A recursive category template contains a checkbox that is bound to a computed property isSelected, that just checks if a string (passed as template property name) is contained in the selectedItems. Clicking a checkbox is extremely slow. Is it possible to improve the performance?
Clicking the checkbox will instantly toggle the checkmark.
It takes over a second for the checkmark to appear.
Note, I've also tried to bind the checkbox directly to the store state via <checkbox v-model="$store.state.selectedItems" but the performance is about the same ...
At first, you should specify what is the performance bottleneck. In your example, the bottleneck is that the app rerender entire DOM tree in every time when the checkbox is toggled. Rerendering >=1000 DOM nodes should be avoided.
Second, you should consider performant data structure since the current data structure can cause the performance drawback.
I tweak your example just as an example for improving response time of checkbox. https://codepen.io/ktsn/pen/rzVgbO?editors=1011
You may want to learn the reactivity system of Vue.js https://vuejs.org/v2/guide/reactivity.html
I'm closing this issue since I don't think we can improve the performance when the app renders such large amount of DOM nodes. The users should consider to eliminate a bottleneck at first.
@ktsn, can you explain why it is that the original version updates the entire DOM tree every click? it is not immediately apparent to me from looking at the two codepens. thanks!
@kpasko Behavior of observable array assumes that any call on changing the array (not the values) triggers notification on subscribers. In this case, there are 2 calls to .push and .splice methods in mutation: both of them change the array structure. While every component has computed property (isSelected) subscribed on the array (it cannot be subscribed on indexOf result), all of the components are being triggered to re-render.
In this case the solutions is to refactor the state to avoid nested data sets: make an array of IDs, which would represent ordering and a flat map of categories, keyed by theirs IDs. Categories themselves should contain references to children with IDs instead of including them. (See https://github.com/paularmstrong/normalizr)
Second to create separate isSelected value for each category in state and implement selectedItems as a getter instead.
// _ = require('lodash')
const state = {
order: ['1', '2', '5', '7', /* ... */, '999'],
items: {
'1': { title: 'Food and drink', isSelected: false, children: ['2', '5'] },
'2': { title: 'Food', isSelected: false, children: ['7'] },
'5': { title: 'Bread and cereals', isSelected: false, children: [] },
/* ... */
},
}
const getItems = (state, ids) => {
return _.map(state.order, (id) => {
let item = state.items[id]
return _.assign({}, item, {
children: item.children.length > 0 ? getItems(state, item.children) : [],
})
})
}
const getters = {
itemsList (state) {
return getItems(state, state.order)
},
selectedItems (state) {
return _.filter(state.order, (id) => state.items[id].isSelected)
},
}
const mutations = {
toggleItem (state, id) {
state.items[id].isSelected = !state.items[id].isSelected
},
}
Dear @rkgrep @katashin
I have a similar experience where I have about 1000 items in an array.
idsArray = [8, 20, 322, 5]itemsLookup = { '8': {itemInfo: ...}, '20': {itemInfo: ...} }Currently I'm doing a map() on the idsArray to show each item in a long list. I have some action that allows an item to be selected and then to be moved up/down with the keyboard.
Moving is currently very slow: splice the id out and splice it back in on a different index.
I think because the whole list has to be re-rendered since the array has changed.
Is there any other way I could do this that gives better performance if my users need to have an ordered list of 1000 items or so?
Most helpful comment
@kpasko Behavior of observable array assumes that any call on changing the array (not the values) triggers notification on subscribers. In this case, there are 2 calls to
.pushand.splicemethods in mutation: both of them change the array structure. While every component has computed property (isSelected) subscribed on the array (it cannot be subscribed onindexOfresult), all of the components are being triggered to re-render.In this case the solutions is to refactor the state to avoid nested data sets: make an array of IDs, which would represent ordering and a flat map of categories, keyed by theirs IDs. Categories themselves should contain references to children with IDs instead of including them. (See https://github.com/paularmstrong/normalizr)
Second to create separate
isSelectedvalue for each category in state and implementselectedItemsas a getter instead.