Vue 中如何控制组件重新渲染的粒度? [英] How to control component re-render granularity in Vue?

查看:21
本文介绍了Vue 中如何控制组件重新渲染的粒度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用一个组件来呈现一个动态表,其中包含许多"行.有时我会添加新行,有时我会修改现有行,但是每当我这样做时,Vue 都会重新渲染整个表,这需要相当长的时间,我想避免这种情况.

我正在使用键和其他东西来加速重新渲染过程,但我仍然觉得这个过程效率低下:Vue 至少会遍历每一行,检查它是否发生了变化.

我知道每次受影响的行.

我如何告诉 Vue 它应该只在重新渲染时打扰那些?

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

OTOH,我还发现了 这个主题 在最后的回复中由 NovaMage 做了很好的解释.如果我读对了,如果我制作行组件并将单个行数据传递给它们会更好吗?因为最初出于性能原因,我并没有将它们精确地制作成组件(实例化数千个组件对于性能来说永远不会太好,对吧?)

无论如何,再说一遍:我怎么能告诉 Vue 它应该只在重新渲染时处理更改的行?

解决方案

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

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

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

Vue.component("row", {道具:[数据"],数据() {返回 {};},模板:`<tr><td><button @click="$emit('update-row', data)">更新行</button></td><td>{{ new Date() }}</td><td>{{ data.id }}</td><td>{{ data.text }}</td></tr>`});常量初始计数 = 2const vm = 新的 Vue({el: '#app',数据() {返回 {计数:初始计数,行:Array.from({长度:初始计数}, (v, i) =>({身份证:我,文本:`文本 - ${i}`}))}},方法: {添加行(){this.rows.push({id:this.count,文本:`文本 - ${this.count} `})this.count++},更新行(行){row.text = row.text + 'U'}},})

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script><div id="应用程序">表格呈现:{{ new Date() }}<button @click="addRow">添加行</button><表格><tr v-for="row in rows" is="row" :data="row" :key="row.id" @update-row="updateRow"/>

I'm using a component to render a dynamic table which has "many" rows. Sometimes I add new rows, sometimes I modify existing rows, but whenever I do any of that, Vue will re-render the entire table and that takes a considerable time, which I would like to avoid.

I'm using keys and stuff to accelerate the re-rendering process, but I still feel the process is inefficient: Vue will at least run through each row, checking whether it has changed or not.

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

I know the rows affected each time.

How can I tell Vue it should only bother with those when it re-renders?

I have considered to have two lists: the stationary one and the changing one, but I have no idea how I could do the <tr v-for="item in two_lists"/> such that Vue would actually ignore the first list when re-rendering.

OTOH, I also found this thread with an excellent explanation by NovaMage in the last reply. If I read that correctly, I would be better off if I made rows components and passed individual row data to them? Because initially I DIDN'T make them components precisely for performance reasons (instantiating thousands of components can't ever be too good for performance, right?)

Anyway, again: How can I tell Vue it should only bother with changed rows when it re-renders?

解决方案

Rendering granularity in Vue is always a component. Component's template is compiled into a render function. When re-render is needed, render function is executed as a whole unit. There are some optimizations the template compiler can do (like hoisting static content for example) and from what I know there are some significant improvements in Vue 3 in this field but I think the optimization of v-for loops is not one of them (ie. rendering only for some items in the loop and skip others)

So yes, creating a Row component might be a solution to optimize rendering performance for some operations at the cost of some memory and time needed for instantiating the components. Adding/removing items from array will probably still result in table component re-render, but unchanged Row components will not re-render even in this case (see demo below)

I do not know what "many" exactly means, but assuming only fraction of the rendered rows is visible on the screen at the time, the best optimization you can do (except of pagination mentioned in comments) is using some kind of "virtual scroll" component - for example 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>

这篇关于Vue 中如何控制组件重新渲染的粒度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆