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>
我正在使用一个组件来呈现具有“许多”行的动态 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>