Vue 如何控制组件重新渲染的粒度?

How to control component re-render granularity in Vue?

我正在使用一个组件来呈现具有“许多”行的动态 table。有时我添加新行,有时我修改现有行,但每当我执行任何这些操作时,Vue 都会重新渲染整个 table 并且这会花费相当长的时间,我想避免这种情况。

我正在使用键和其他东西来加速重新渲染过程,但我仍然觉得这个过程效率低下:Vue 至少 运行 遍历每一行,检查它是否已更改。

<tr v-for="row in rows" :key="row['id']">....</tr>

我知道每次受影响的行。

我如何告诉 Vue 它应该只在重新渲染时才处理这些问题?

我考虑过有两个列表:固定列表和变化列表,但我不知道如何做 <tr v-for="item in two_lists"/> 这样 Vue 在重新渲染时实际上会忽略第一个列表。

OTOH,我还发现 this thread 在上一个回复中 NovaMage 给出了很好的解释。如果我没看错的话,如果我制作行组件并将单独的行数据传递给它们会更好吗?因为最初我并没有完全出于性能原因将它们制作成组件(实例化数千个组件对性能来说永远不会太好,对吧?)

再说一次:我如何告诉 Vue 它在重新呈现时只需要处理更改的行?

Vue 中的渲染粒度始终是一个组件。组件的模板被编译成渲染函数。当需要重新渲染时,渲染函数作为一个整体执行。模板编译器可以做一些优化(例如提升静态内容),据我所知,Vue 3 在这个领域有一些重大改进 我认为优化 v-for 循环不是其中之一(即只渲染循环中的某些项目并跳过其他项目)

所以是的,创建一个 Row 组件可能是一种优化某些操作的渲染性能的解决方案,代价是实例化组件所需的一些内存和时间。 Adding/removing 数组中的项目可能仍会导致 table 组件重新渲染,但未更改的 Row 组件即使在这种情况下也不会重新渲染(参见下面的演示)

我不知道“很多”到底是什么意思,但假设当时只有一小部分渲染行在屏幕上可见,您可以做的最好的优化(评论中提到的分页除外)是使用一些一种“虚拟滚动”组件 - 例如 vue-virtual-scroller

Vue.component("row", {
  props: ["data"],
  data() {
    return {};
  },
  template: `
  <tr>
    <td><button @click="$emit('update-row', data)">Update row</button></td>
    <td>{{ new Date() }}</td>
    <td>{{ data.id }}</td>
    <td>{{ data.text }}</td>        
  </tr>
`
});

const initialCount = 2
const vm = new Vue({
  el: '#app',
  data() {
    return {
      count: initialCount,
      rows: Array.from({
        length: initialCount
      }, (v, i) => ({
        id: i,
        text: `text - ${i}`
      }))
    }
  },
  methods: {
    addRow() {
      this.rows.push({
        id: this.count,
        text: `text - ${this.count} `
      })
      this.count++
    },
    updateRow(row) {
      row.text = row.text + 'U'
    }
  },  
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  Table rendered: {{ new Date() }}
  <button @click="addRow">Add row</button>  
  <table>  
    <tr v-for="row in rows" is="row" :data="row" :key="row.id" @update-row="updateRow" />
  </table>
</div>