Как в Vue Js отключить режим "Обновления на месте"

<component v-for="(element,key) in bloks[key_block]" :is="element" :key_block="key_block":key="key"></component>

делаю цикл компонентов, цикл нормально отображается, но при попытке удалить один из элементов цикла, удаляется последний, а не выбранный. Если циклить просто html код то эту проблему можно обойти просто добавив :key, но при цикле компонентов это не работает, подскажите пожалуйста как быть?


Ответы (2 шт):

Автор решения: Mikalai Parakhnevich

При манипуляциях с массивами следуйте рекомендациям в руководстве, а именно:

  • используйте методы изменения массива, которые vue дополняет(оборачивает) системой реактивности, чтобы запускать обновления представления (push, pop, shift, unshift, splice, sort, reverse).
  • Обратите внимание, на особенности отслеживания изменений массива, а именно следует учитывая, что vue не может отследить изменения в массиве, если вы совершаете прямую установку элемента по индексу или явно изменяете длину массива.

Полезные ссылки:

Вам достаточно при манипуляциях с данными из массива, явно задавать индекс или другое свойство объекта, по которому вы сможете идентифицировать объект из массива и удалить его. И чтобы vue вел себя более предсказуемо при обновлении представлений и рендеринге, используйте v-for с v-bind:key

Приведу пример, может будет вам полезен:

Vue.component('Test', {
  props: ['title'],
  template: `
    <div>Компонент - {{ title }}</div>
  `
})

new Vue({
  el: '#app',
  data: {
    components: [{
        id: 1,
        name: 'Test',
        title: 'component 1'
      },
      {
        id: 2,
        name: 'Test',
        title: 'component 2'
      },
    ]
  },
  computed: {
    allIds() {
      return this.components.map(x => x.id)
    }
  },
  methods: {
    removeComponent(id) {
      const index = this.components.findIndex(x => x.id === id)
      this.components.splice(index, 1)
    },
    addComponent() {
      const id = this.allIds.length ? Math.max(...this.allIds) + 1 : 1
      this.components.push({
        id,
        title: `component ${id}`,
        name: 'Test'
      })
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
  <template v-for="element in components">
    <component :is="element.name" :key="element.id" :title="element.title"></component>
    <button @click="removeComponent(element.id)">удалить {{ element.id }} компонент</button>
  </template>
  <hr>
  <button @click="addComponent">+ компонент</button>
</div>

И еще пример с использованием vue3 (обратите внимание, 2 и 3 версии во много отличаются. Все отличия и правила миграции вы можете просмотреть в Миграция с vue2 ):

const ob = {

  data() {
    return {
      components: [{
        id: 1,
        name: 'element-link',
        title: 'test'
      }]

    }
  },
  computed: {
    allIds() {
      return this.components.map(x => x.id)
    }
  },
  methods: {
    removeComponent(id) {
      const index = this.components.findIndex(x => x.id === id)
      this.components.splice(index, 1)
    },
    addComponent() {
      const id = this.allIds.length ? Math.max(...this.allIds) + 1 : 1
      this.components.push({
        id,
        title: `component ${id}`,
        name: 'element-link'
      })
    }
  }
};
const app = Vue.createApp(ob);
app.component('element-link', {
  props: ['title'],
  template: `
    <div>Компонент - {{ title }} <input type="text" :value="title"></div>
  `
})

app.mount('#app');
<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="https://unpkg.com/vue@next"></script>

</head>

<body>
  <div id="app">
    <template v-for="element in components" :key="element.id">
        <component :is="element.name" :title="element.title"></component>
        <button @click="removeComponent(element.id)">удалить {{ element.id }} компонент</button>
    </template>
    <hr>
    <button @click="addComponent">+ компонент</button>
  </div>
</body>

</html>

→ Ссылка
Автор решения: php777

пробую, по примеру, но увы

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://unpkg.com/vue@next" ></script>

</head>
<body>
<div id="app">
    <template v-for="element in components">
        <component :is="element.name" :key="element.id" :title="element.title"></component>
        <button @click="removeComponent(element.id)">удалить {{ element.id }} компонент</button>
    </template>
    <hr>
    <button @click="addComponent">+ компонент</button>
</div>
</body>
</html>

<script>

  const ob = {

      data () { return {
          components: [{
              id: 1,
              name: 'element-link',
              title: 'test' }]

      } },
        el: '#app',
      computed: {
          allIds() {
              return this.components.map(x => x.id)
          }
      },
      methods: {
          removeComponent(id) {
              const index = this.components.findIndex(x => x.id === id)
              this.components.splice(index, 1)
          },
          addComponent() {
              const id = this.allIds.length ? Math.max(...this.allIds) + 1 : 1
              this.components.push({
                  id,
                  title: `component ${id}`,
                  name: 'test'
              })
          }
      }
  };
  const app = Vue.createApp(ob);
  const test = app.component('test',  {
      props: ['title'],
      template: `
    <div>Компонент - {{ title }} <input type="text"></div>
  `
  })

  app.mount('body');

</script>

→ Ссылка