Vue 3 — Как использовать введенный список в цикле v-for?

#vue.js #vuejs3

Вопрос:

У меня есть приложение, которое, помимо прочего, отслеживает элементы в списках. Следующий пример, небольшое расширение примера документов (использование props ), работает правильно.

index.html

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Vue List Component Test</title>
    <script src="https://unpkg.com/vue@3"></script>
</head>
<body>
    <div id="app"></div>
    <script src="./app.js"></script>
</body>
</html>
 

app.js (с реквизитом)

 const MyApp = {
    data() {
        return {
            items: []
        }
    },
    template: `<list-count></list-count><list :itemList="items"></list>`,
    methods: {
        addItem(i) {
            this.items.push(i)
        }
    },
    provide() {
        return {
            listLength: Vue.computed(() => this.items.length)
        }
    },
}

const app = Vue.createApp(MyApp)

const ItemListCount = {
    inject: ["listLength"],
    template: `<p> {{ this.listLength }}</p>`
}

const ItemList = {
    props: ["itemList"],
    template:`<ul>
                  <list-item v-for="(item, index) in itemList" :i="item" :idx="index"></list-item>
              </ul>`
}

const ItemListItem = {
    props: ["i", "idx"],
    template: `<li>{{ idx }}, {{ i.message }}</li>`
}

app.component("list", ItemList)
app.component("list-item", ItemListItem)
app.component("list-count", ItemListCount)
const vm = app.mount('#app')
 

Поскольку списки элементов находятся в компонентах, которые подлежат маршрутизации, я бы предпочел использовать Provide/Inject вместо того, чтобы выяснять, как передавать реквизиты по цепочке и через маршрутизатор. Следующее app.js , которое использует provide/inject вместо реквизитов для списка, не работает.

app.js (с предоставлением/введением) … обратите внимание на удаление :itemList привязки, добавление itemList in provide() и изменение ItemList компонента для использования inject вместо props .

 const MyApp = {
    data() {
        return {
            items: []
        }
    },
    template: `<list-count></list-count><list></list>`,
    methods: {
        addItem(i) {
            this.items.push(i)
        }
    },
    provide() {
        return {
            listLength: Vue.computed(() => this.items.length),
            itemList: Vue.computed(() => this.items)
        }
    },
}

const app = Vue.createApp(MyApp)

const ItemListCount = {
    inject: ["listLength"],
    template: `<p> {{ this.listLength }}</p>`
}

const ItemList = {
    inject: ["itemList"],
    template:`<ul>
                  <list-item v-for="(item, index) in itemList" :i="item" :idx="index"></list-item>
              </ul>`
}

const ItemListItem = {
    props: ["i", "idx"],
    template: `<li>{{ idx }}, {{ i.message }}</li>`
}

app.component("list", ItemList)
app.component("list-item", ItemListItem)
app.component("list-count", ItemListCount)
const vm = app.mount('#app')
 

Вышесказанное вызывает следующую ошибку в консоли:

Ошибка неперехваченного типа: i не определен

Я предполагаю, что ошибка заключается в том, что v-for цикл неправильно работает с a, в то inject: ["itemList"] время как с a он, похоже, работает нормально props: ["itemList"] . Я не могу найти никаких соответствующих документов, которые могли бы объяснить, почему это так. Как исправить версию provider/inject?

Ответ №1:

Похоже, у вас есть какие-то ненужные вещи внутри вашей функции предоставления.

Это должно выполнить свою работу:

 provide() {
    return {
      itemList: this.items
    };
  }
 

Смотрите этот пример рабочего кода

Обновить

Кроме того, я предполагаю, что вы используете этот код в качестве примера какой-то более сложной операции, которая фактически оправдывает использование provide/inject. В противном случае это излишество для компонента списка, и вы должны упростить его следующим образом

 const MyApp = {
  data() {
    return {
      items: []
    };
  },
  template: `
  <button @click="addItem(Math.floor(Math.random() * 100))">Add Item</button>
  <list :items="items"></list>`,
  methods: {
    addItem(i) {
      this.items.push(i);
    }
  }
};

const app = Vue.createApp(MyApp);

const ItemList = {
  props: { items: Array },
  template: `
  <p>{{ items.length }}</p>
  <ul>
  <li v-for="(item, index) in items">{{ index   ', '   item }}</li>
  </ul>`
};

app.component("list", ItemList);

app.mount("#app");
 

Комментарии:

1. Реальное приложение включает в себя маршрутизацию, поэтому ItemList это один из нескольких компонентов, которые могут появиться в главном окне содержимого. Мне легче думать о том , как бы я предоставлял/вводил, а не передавал свойства <router-view></router-view> , я думаю.