1👍
This excellent article suggests that the raw number of DOM nodes has the biggest impact on performance. That said, I didn’t experience any real performance bottlenecks in the sample app that I built to learn more about your problem. The entire page with the table loaded in about 1.25s (from localhost), regardless of whether it was in dev mode or it was a production build. The JavaScript console timer reported that expanding or contracting ALL 100 rows simultaneously only took an average of about 0.3s. Bottom line, I think you can achieve the optimizations you’re looking for and not have to give up the conveniences of Vuetify.
Recommendations
- Consider displaying fewer rows at one time (biggest expected impact)
- Streamline your template to use as few elements as possible, only display data that’s really necessary to users. Do you really need a
v-data-table
inside av-data-table
? - Streamline your data model and only retrieve the bare minimum data you need to display the table. As @Codeply-er suggested, the size and complexity of your data could be causing this strain
Testing Method
Here’s what I did. I created a simple Vue/Vuetify app with a VDataTable
with 100 expandable rows. (The data was pulled from the random user API). I used this method to count DOM nodes. Here are some of the parameters/info:
- Rows: 100
- Columns: 5 + the expansion toggler
- Expansion row content: a
VSimpleTable
with the user’s picture and address - Size of a single JSON record returned from the API: ~62 lines (about half the size of your sample object above)
- Vue v2.6.11
- Vuetify v2.3.0-beta.0
(I realize this just came out, but I don’t think you’d have different results using v2.2.x) - App was built with
vue create myapp
andvue add vuetify
VDataTable
actually adds/removes the expansion rows from the DOM whenever the rows are expanded/contracted
Here’s some approximate stats on the result (these numbers fluctuated slightly in different conditions–YMMV):
- 773 (~7/row): number of DOM nodes in 100 rows/5 columns without expansion enabled
- 977 (+2/row): number of nodes with expansion enabled
- 24: number of nodes added to the table by expanding a single row
- 3378 (+26/row): total nodes with ALL rows expanded
- ~1.25s to load the entire page on a hard refresh
- ~0.3s to expand or contract ALL of the nodes simultaneously
- Sorting the columns with the built-in sorting tools was fast and very usable
Here’s code of the App.vue
page of my app. The v-data-table
almost the only component on the page (except the toggle button) and I didn’t import any external components.
<template>
<v-app>
<v-btn
color="primary"
@click="toggleExpansion"
>
Toggle Expand All
</v-btn>
<v-data-table
:expanded.sync="expanded"
:headers="headers"
:items="items"
item-key="login.uuid"
:items-per-page="100"
show-expand
>
<template #item.name="{ value: name }">
{{ name.first }} {{ name.last }}
</template>
<template #expanded-item="{ headers, item: person }">
<td :colspan="headers.length">
<v-card
class="ma-2"
max-width="500px"
>
<v-row>
<v-col cols="4">
<v-img
:aspect-ratio="1"
contain
:src="person.picture.thumbnail"
/>
</v-col>
<v-col cols="8">
<v-simple-table>
<template #default>
<tbody>
<tr>
<th>Name</th>
<td class="text-capitalize">
{{ person.name.title }}. {{ person.name.first }} {{ person.name.last }}
</td>
</tr>
<tr>
<th>Address</th>
<td class="text-capitalize">
{{ person.location.street.number }} {{ person.location.street.name }}<br>
{{ person.location.city }}, {{ person.location.state }} {{ person.location.postcode }}
</td>
</tr>
<tr>
<th>DOB</th>
<td>
{{ (new Date(person.dob.date)).toLocaleDateString() }} (age {{ person.dob.age }})
</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-col>
</v-row>
</v-card>
</td>
</template>
</v-data-table>
</v-app>
</template>
<script>
import axios from 'axios'
export default {
name: 'App',
data: () => ({
expanded: [],
headers: [
{ text: 'Name', value: 'name' },
{ text: 'Gender', value: 'gender' },
{ text: 'Phone', value: 'phone' },
{ text: 'Cell', value: 'cell' },
{ text: 'Country', value: 'nat' },
{ text: '', value: 'data-table-expand' },
],
items: [],
}),
created () {
axios.get('https://randomuser.me/api/?seed=stackoverflow&results=100')
.then(response => {
this.items = response.data.results
})
},
methods: {
toggleExpansion () {
console.time('expansion toggle')
this.expanded = this.expanded.length ? [] : this.items
console.timeEnd('expansion toggle')
},
},
}
</script>
You can see a working demo in this codeply. Hope this helps!