【问题标题】:VueJS, Vuetify, data-table - expandable, performance problemVueJS,Vuetify,数据表 - 可扩展,性能问题
【发布时间】:2020-08-11 03:37:28
【问题描述】:

我的 VueJS 和 Vuetify 项目有问题。 我想创建一个带有可扩展行的表。这将是一张订单表,可以查看每个订单的购买产品。对于一页,它应该显示至少 100 行订单。 为此,我使用了 Vuetify 框架中的<v-data-table>。


有什么问题?

在准备好一切之后,我意识到它可以工作,但是对于每一行的扩展,我必须等待几秒钟(它太长了 - 它必须是一个快速的系统)。并且为了扩展所有可见记录,需要等待超过 20 秒整个页面滞后。


我尝试了什么?

我从标准的 Vuetify <v-data-table> 和 show-expand 属性和 expanded-item 插槽开始 - 这是我的第一次尝试 - 最慢。 其次,我尝试自己创建 - 但使用 Vuetify:

<v-data-table>
 <template v-slot:item="{item}">
   <td @click="item.expanded = !item.expanded">expand / hide</td>
   <!--- [my table content here - too long to post it here] -->
   <tr v-if="item.expanded">
     <td :colspan="headers.length">
     <v-data-table>
       <!--- [content of the nested table - also too long to post it here] -->
     </v-data-table>
   </tr>
 </template>
</v-data-table>

有趣的是 - 我意识到 v-if 比 v-show 工作得更快,这是一个奇怪的事实,因为我认为将 display: none 更改为空应该比将整个对象添加/删除到 DOM 问题更少.

这种方法比第一种方法快一点,但还是太慢了。

我发现了一个提示,可以为我的表中的每个 v-btn 设置 :ripple="false",我做到了 - 有所帮助,但只是一点点。 一切都在 Chrome 和 Firefox 上进行了测试,在三台装有 Windows 和 Linux Fedora 的设备以及两部 Android 智能手机上进行了测试。


我还应该做什么?

提前谢谢你!

【问题讨论】:

  • 数据项是什么样的?您是否在扩展项目时延迟加载它们?
  • 在 VueJS 的挂载部分,我运行 Vuex 操作,它会立即从数据库中获取带有交易的订单,因此当订单展开时,交易将从状态加载
  • 真的很难说没有看到实际的数据结构。项目对象是大、复杂还是嵌套严重?您能否简化返回的初始数据库结构,然后随着行的展开而按需延迟加载项目详细信息?
  • 好的,这是一份包含所有数据的订单:pastebin。在我的订单数组中应该至少有 100 行这样的。
  • 我尝试使用纯 js onClick="" 事件 - 简单的函数添加/删除 display: none css 属性。它很快就起作用了。但是如何在 VueJS 中实现呢?

标签: javascript performance vue.js vuejs2 vuetify.js


【解决方案1】:

This excellent article suggests that the raw number of DOM nodes has the biggest impact on performance。也就是说,在我构建的示例应用程序中,我没有遇到任何真正的性能瓶颈,以了解有关您的问题的更多信息。包含表的整个页面在大约 1.25 秒内加载(来自本地主机),无论它是处于开发模式还是生产版本。 JavaScript console timer 报告说,同时扩展或收缩所有 100 行平均只需要大约 0.3 秒。最重要的是,我认为您可以实现您正在寻找的优化,而不必放弃 Vuetify 的便利。

建议

  1. 考虑一次显示更少的行(最大的预期影响)
  2. 简化您的模板以使用尽可能少的元素,仅显示用户真正需要的数据。你真的需要v-data-table inside v-data-table吗?
  3. 简化数据模型,只检索显示表格所需的最少数据。正如@Codeply-er 所建议的那样,您的数据的大小和复杂性可能会导致这种压力

测试方法

这就是我所做的。我创建了一个简单的 Vue/Vuetify 应用程序,其中包含一个 VDataTable 和 100 个可扩展行。 (数据来自the random user API)。我使用this method 来计算 DOM 节点。以下是一些参数/信息:

  • 行数:100
  • 列:5 + 扩展切换器
  • 扩展行内容:一个VSimpleTable,带有用户的图片和地址
  • 从 API 返回的单个 JSON 记录的大小:~62 行(大约是上面示例对象大小的一半)
  • Vue v2.6.11
  • Vuetify v2.3.0-beta.0
    (我意识到这刚刚发布,但我认为使用 v2.2.x 不会有不同的结果)
  • 应用程序是使用vue create myapp 和vue add vuetify 构建的
  • VDataTable 实际上在扩展/收缩行时从 DOM 中添加/删除扩展行

以下是结果的一些近似统计数据(这些数字在不同条件下略有波动--YMMV):

  • 773(~7/行):100 行/5 列中的 DOM 节点数未启用扩展
  • 977(+2/行):启用扩展的节点数
  • 24:通过展开单个行
  • 添加到表中的节点数
  • 3378(+26/行):所有行已展开的节点总数
  • ~1.25s 以硬刷新加载整个页面
  • ~0.3s 同时扩展或收缩所有节点
  • 使用内置排序工具对列进行排序非常快速且非常实用

这是我的应用程序的App.vue 页面的代码。 v-data-table 几乎是页面上唯一的组件(除了切换按钮),我没有导入任何外部组件。

<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。希望这会有所帮助!

【讨论】:

  • 非常感谢!我精简了我的代码,简化了数据模型,并用我自己的替换了一些 Vuetify 组件——比原来的更轻,现在它工作得更好。设计起来并不难,但老实说,我希望有一个更简单、更省力的解决方案;),你的回答证明这是唯一明智的选择。谢谢!
猜你喜欢
  • 2019-06-17
  • 2021-10-20
  • 2011-06-05
  • 2021-04-18
  • 1970-01-01
  • 2012-01-31
  • 2019-10-31
  • 1970-01-01
  • 2018-06-19
相关资源
最近更新 更多