首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >提高具有自定义单元格和异步数据加载的v数据表的性能

提高具有自定义单元格和异步数据加载的v数据表的性能
EN

Stack Overflow用户
提问于 2020-06-29 04:38:16
回答 1查看 1K关注 0票数 2

我正在用v数据表创建一个页面。该表的某些内容正在挂载的阶段加载,但是在呈现整个表后,通过异步API调用在后台逐行加载一列的数据。表行也应该基于API调用返回的数据进行着色。

我已经开发了这个页面,但是遇到了一个问题--当表中包含由item槽重新定义的组合单元格(例如,带有图标、工具提示或跨范围的单元格)时,表行更新时间将显著增加。

根据业务逻辑,页面可能包含大量行,但我不能使用v数据表分页来减少一个页面上的条目计数。

问题是--我如何用尽可能少的性能损失来更新行(实际上,只更新它的颜色和一个单元格值)?

有这个问题的人。将数据加载到页面的方式在这个Codepen中完全保留下来,但是API调用被固定超时的承诺所取代。

这个问题仍然存在于Codepen。默认情况下,对100个项的所有请求都在12-13秒内传递(页面底部有一个计数器)。当我最后评论td时,它们在7-8秒内就通过了.当我注释掉另一个td (结束时的第二个)时,它们将在6秒内被传递。当我将项目计数增加到1000时,行更新时间也会增加。

代码语言:javascript
复制
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);
      });
    }
  }
})
代码语言:javascript
复制
<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>

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-06-30 07:10:10

如果将DOM封装在组件中,Vue似乎可以更有效地更新DOM(对不起,我不知道为什么)。

这是您在JSFiddle中的原始代码。它将使用大约12-13秒。

然后我创建一个组件来包装整个tr

代码语言:javascript
复制
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看起来会是:

代码语言:javascript
复制
<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上。

代码语言:javascript
复制
...
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 (数学上)。

我希望这能帮上忙。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62631309

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档