Почему Vue обновляет все компоненты, а не только измененный компонент в v-for?

У меня есть v-for список, содержащий компонент. Внутри компонента я отображаю <div> или <input> в зависимости от значения свойства. Проблема в том, что когда я изменяю значение свойства, обновляются все элементы в v-for, а не только компонент, который был изменен. Это не проблема для небольших приложений, но я заметил значительное снижение производительности при работе с большими наборами данных.

Итак, основной вопрос:

Как избежать рендеринга всех компонентов, если обновлен только один компонент?

Я поместил все это в JSFiddle здесь. Обратите внимание, что когда вы нажимаете кнопку, чтобы показать ввод C-компонента, все компоненты повторно отрисовываются (отображаются в консоли), а не только C-компонент.

HTML

<div id="app">
<button @click="showinput = 'C'">
Show C input
</button>
<br>
<br>
<div v-for="item in list" :key="item.id">
  <list-item :item=item :showinput="showinput"></list-item>
  </div>
</div>

<template id="list-item">  <span>  <div v-if="showinput !== item.name">
{{item.name}}</div>
<input v-else
    type="text"
    v-model.lazy="item.name"  >
</span>
</template>

JS

Vue.component('list-item', {
  template: '#list-item',
  props: ['item', 'showinput'],
  data () {
  return   {

  }},
  beforeUpdate() {
    console.log("Updating " + this.item.name)
  }
});

// create a new Vue instance and mount it to our div element above with the id of app
var vm = new Vue({
  el: '#app',
  data: {
  list: [{name: "A", id: 1}, {name: "B", id: 2}, {name: "C", id: 3},],
      showinput: "X"
  }
});

person Gnopps    schedule 12.03.2019    source источник


Ответы (3)


Все ваши компоненты используют одну и ту же переменную showinput, и поэтому все они обновляются. Неважно, что у вас отображается только одно сообщение, vue этого не знает. На самом деле вы все еще визуализируете промежутки, но они просто пусты.

Что вам следует сделать, так это отфильтровать данные и использовать этот отфильтрованный массив в вашем v-for.

computed: {
  filteredList: function () {
     return this.list.filter( item => item.name === this.showInput )
   }
 }

А потом в твоем v-for

<div v-for=“item in filteredList” ...

LE

Если вы хотите показать всю информацию, но изменить представление, вы можете разделить list-item компонент на 2 компонента, один для метки, а другой - для ввода. Затем используйте : is, чтобы выбрать, какой шаблон использовать (или даже v-if). Я создал эту скрипку, чтобы увидеть ее в действии. Обратите внимание, что onBeforeUpdate больше не будет вызываться, потому что vue воссоздает компонент.

Похоже, что в vue 3 это можно исправить, поскольку он будет поддерживать частичный рендеринг компонента.

person Radu Diță    schedule 12.03.2019
comment
Спасибо! Я понимаю суть проблемы. Однако действительно ли рассчитанный фильтр подходит? Это отфильтрует некоторые данные: я хочу, чтобы отображались все данные - только в разных формах. Может ли решение иметь один дочерний компонент с полем ввода, один без, а затем выполнить v-if в родительском элементе, чтобы решить, какой из них показывать? - person Gnopps; 12.03.2019
comment
Вы можете использовать дополнительный фильтр, который использует! == вместо ===. Но это другая проблема, и вам, вероятно, стоит задать еще вопросы по SO. - person Radu Diță; 12.03.2019
comment
Спасибо, но я не слежу за этим. Я хочу отображать все данные в одном и том же порядке - только некоторые как поле ввода, а некоторые как диапазон. Как тогда я могу использовать вычисляемый фильтр? Не могли бы вы указать мне правильное направление того, что мне следует искать? Тем временем я попробовал свое собственное предложение с двумя разными дочерними компонентами и v-if, и хотя производительность немного лучше, это не имеет большого значения. - person Gnopps; 13.03.2019
comment
Спасибо за помощь! - person Gnopps; 16.03.2019

Когда вы нажимаете кнопку «Показать C», вы обновляете переменную showInput. Теперь эта showInput переменная отправляется в качестве опоры каждому <list-item /> в вашем v-for, и тем самым вы обновляете все компоненты.

person Muhamed Ahmatović    schedule 12.03.2019

В вашем компоненте list-item у вас есть свойство showinput. Таким образом, каждый раз, когда значение этого свойства изменяется, ваш компонент будет обновляться.

Теперь всякий раз, когда вы нажимаете «Показать ввод C», вы обновляете «showinput», и поскольку вы используете «v-for» для компонента «list-item», «showinput» присоединяется к каждому «list-item» и, следовательно, ко всем компонентам. будет обновлено.

person Ronin Kr    schedule 12.03.2019