我正在用v数据表创建一个页面。该表的某些内容正在挂载的阶段加载,但是在呈现整个表后,通过异步API调用在后台逐行加载一列的数据。表行也应该基于API调用返回的数据进行着色。
我已经开发了这个页面,但是遇到了一个问题--当表中包含由item槽重新定义的组合单元格(例如,带有图标、工具提示或跨范围的单元格)时,表行更新时间将显著增加。
根据业务逻辑,页面可能包含大量行,但我不能使用v数据表分页来减少一个页面上的条目计数。
问题是--我如何用尽可能少的性能损失来更新行(实际上,只更新它的颜色和一个单元格值)?
有有这个问题的人。将数据加载到页面的方式在这个Codepen中完全保留下来,但是API调用被固定超时的承诺所取代。
这个问题仍然存在于Codepen。默认情况下,对100个项的所有请求都在12-13秒内传递(页面底部有一个计数器)。当我最后评论td时,它们在7-8秒内就通过了.当我注释掉另一个td (结束时的第二个)时,它们将在6秒内被传递。当我将项目计数增加到1000时,行更新时间也会增加。
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
headers: [
{
text: 'Dessert (100g serving)',
value: 'name',
},
{ text: 'Second name', value: 'secondName' },
{ text: 'Fat (g)', value: 'fat' },
{ text: 'Carbs (g)', value: 'carbs' },
{ text: 'Protein (g)', value: 'protein' },
{ text: 'Max value', value: 'maxValue' },
{ text: 'Actions', value: 'id' },
],
desserts: [],
timerStart: null,
loadingTime: null,
}
},
created() {
this.generateDesserts();
},
mounted() {
this.countMaxValues(this.desserts).then(() => {
this.loadingTime = (Date.now() - this.timerStart) / 1000;
});
},
methods: {
generateDesserts() {
let dessertNames = [
'Frozen Yogurt ',
'Ice cream sandwich ',
'Eclair',
'Cupcake',
'Gingerbread',
'Jelly bean',
'Lollipop',
'Honeycomb',
'Donut',
'KitKat',
null
];
for (let i = 0; i < 100; i++) {
let dessert = {
id: i,
name: dessertNames[Math.floor(Math.random() * dessertNames.length)],
secondName: dessertNames[8 + Math.floor(Math.random() * (dessertNames.length - 8))],
fat: Math.random() * 100,
carbs: Math.floor(Math.random() * 100),
protein: Math.random() * 10
};
this.desserts.push(dessert);
}
},
async countMaxValues(array) {
this.timerStart = Date.now();
for (const item of array) {
await this.countMaxValue(item).catch(() => {
//Even when one request throws error we should not stop others
})
}
},
async countMaxValue(item) {
await new Promise(resolve => setTimeout(resolve, 50)).then(() => {
let maxVal = Math.random() * 100;
item.maxValue = maxVal < 20 ? null : maxVal;
this.desserts.splice(item.id, 1, item);
});
}
}
})<div id="app">
<v-app id="inspire">
<v-data-table
:headers="headers"
:items="desserts"
:footer-props='{
itemsPerPageOptions: [-1],
prevIcon: null,
nextIcon: null,
}'
>
<template v-slot:item="props">
<tr :style="{
background: (props.item.maxValue !== null && (props.item.carbs < props.item.maxValue))
? '#ffcdd2'
: (
(props.item.maxValue !== null && (props.item.carbs > props.item.maxValue)
? '#ffee58'
: (
props.item.maxValue === null ? '#ef5350' : 'transparent'
)
)
)}">
<td>{{ props.item.name || '—' }}</td>
<td>{{ props.item.secondName || '—' }}</td>
<td>{{ props.item.fat }}</td>
<td>{{ props.item.carbs }}</td>
<td>{{ props.item.protein }}</td>
<td>
<span>
{{ props.item.maxValue || '—' }}
</span>
<v-btn v-if="props.item.name && props.item.maxValue" icon>
<v-icon small>mdi-refresh</v-icon>
</v-btn>
</td>
<td class="justify-center text-center" style="min-width: 100px">
<v-tooltip bottom v-if="props.item.name && props.item.secondName">
<template v-slot:activator="{ on }">
<v-icon v-on="on"
class="mr-2"
small
>
format_list_numbered_rtl
</v-icon>
</template>
<span>Some action tooltip</span>
</v-tooltip>
<v-tooltip bottom v-if="props.item.name && props.item.secondName">
<template v-slot:activator="{ on }">
<v-icon v-on="on"
class="mr-2"
small
>
edit
</v-icon>
</template>
<span>Edit action tooltip</span>
</v-tooltip>
<v-tooltip bottom v-if="props.item.name === 'KitKat'">
<template v-slot:activator="{ on }">
<v-icon v-on="on"
small
>
delete
</v-icon>
</template>
<span>Delete action tooltip</span>
</v-tooltip>
</td>
</tr>
</template>
</v-data-table>
<p>{{ "Page loading time (sec): " + (loadingTime || '...') }}</p>
</v-app>
</div>
发布于 2020-06-30 07:10:10
如果将DOM封装在组件中,Vue似乎可以更有效地更新DOM(对不起,我不知道为什么)。
这是您在JSFiddle中的原始代码。它将使用大约12-13秒。
然后我创建一个组件来包装整个tr
const Tr = {
props: {
item: Object
},
template: `
<tr>
... // change props.item to item
</tr>
`
}
new Vue({
el: '#app',
vuetify: new Vuetify(),
components: {
'tr-component': Tr // register Tr component
},
...
async countMaxValue(item) {
await new Promise(resolve => setTimeout(resolve, 50)).then(() => {
let maxVal = Math.random() * 100;
// update entire object instead of one property since we send it as object to Tr
let newItem = {
...item,
maxValue: maxVal < 20 ? null : maxVal
}
this.desserts.splice(newItem.id, 1, newItem);
});
}
})你的html看起来会是:
<v-data-table
:headers="headers"
:items="desserts"
:footer-props='{
itemsPerPageOptions: [-1],
prevIcon: null,
nextIcon: null,
}'>
<template v-slot:item="props">
<tr-component :item='props.item'/>
</template>
</v-data-table>结果将使用6-7秒左右的时间来更新DOM,仅需1-2秒。
或者,如果您发现您的函数触发得非常快(在您的示例中使用50 my,在我看来太快了),您可以尝试将它控制在更少的更新DOM上。
...
methods: {
async countMaxValue(item) {
await new Promise(resolve => setTimeout(resolve, 50)).then(() => {
let maxVal = Math.random() * 100;
let newItem = {
...item,
maxValue: maxVal < 20 ? null : maxVal
}
this.changes.push(newItem) // keep newItem to change later
this.applyChanges() // try to apply changes if it already schedule it will do nothing
});
},
applyChanges () {
if (this.timeoutId) return
this.timeoutId = setTimeout(() => {
while (this.changes.length) {
let item = this.changes.pop()
this.desserts.splice(item.id, 1, item)
}
this.timeoutId = null
}, 1500)
}
}结果将使用5-6秒左右的时间,但正如您所看到的,它不会立即更新。
或者,您可以尝试并行调用API,例如10次请求,您可以将等待时间从100 * 50 ms减少到大约10 * 50 ms (数学上)。
我希望这能帮上忙。
https://stackoverflow.com/questions/62631309
复制相似问题