withv-forCorresponds an array to a set of elements

We use the V-for instruction to render from an array of options. The V-for directive requires a special syntax of the form item in Items, which is an array of source data and an alias for an iteration of array elements.

<ul id="example-1">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
Copy the code
var example1 = new Vue({
  el: '#example-1',
  data: {
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
Copy the code

In the V-for block, we have full access to the parent scoped properties. V-for also supports an optional second argument to the index of the current item.

<ul id="example-2">
  <li v-for="(item, index) in items">
    {{ parentMessage }} - {{ index }} - {{ item.message }}
  </li>
</ul>
Copy the code
var example2 = new Vue({
  el: '#example-2',
  data: {
    parentMessage: 'Parent',
    items: [
      { message: 'Foo' },
      { message: 'Bar' }
    ]
  }
})
Copy the code

You can also use of instead of in as a separator, since it is the syntax closest to a JavaScript iterator:

<div v-for="item of items"></div>
Copy the code

V-for of an object

<ul id="v-for-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
Copy the code
new Vue({
  el: '#v-for-object',
  data: {
    object: {
      firstName: 'John',
      lastName: 'Doe',
      age: 30
    }
  }
})
Copy the code

You can also supply the second parameter as the key name:

<div v-for="(value, key) in object">
  {{ key }}: {{ value }}
</div>
Copy the code

The third parameter is the index:

<div v-for="(value, key, index) in object">
  {{ index }}. {{ key }}: {{ value }}
</div>
Copy the code

When traversing an Object, the result of object.keys () is traversed, but there is no guarantee that the result will be consistent across different JavaScript engines.

key

When vue.js is updating a rendered element list with V-for, it defaults to a “reuse in place” strategy. If the order of the data items is changed, Vue will not move the DOM elements to match the order of the data items, but will simply reuse each element here and make sure it shows every element that has been rendered under a specific index. Vue 1.x track-by=”$index”

This default mode is efficient, but only for list rendering output that does not depend on child component state or temporary DOM state (for example, form input values).

To give Vue a hint that it can track the identity of each node to reuse and reorder existing elements, you need to provide a unique key attribute for each item. The ideal key value is a unique ID for each item. This particular attribute is equivalent to Vue 1.x track-by, but it works like an attribute, so you’ll need to v-bind the dynamic value (using the shorthand here) :

<div v-for="item in items" :key="item.id"> <! </div>Copy the code

It is recommended to provide keys whenever possible with V-for, unless traversing the output DOM content is very simple or you deliberately rely on default behavior for performance gains.

Because it is a general mechanism for Vue to identify nodes, key is not specifically associated with V-for and has other uses, which we will see in a later guide.

Array update detection

Variation method

Vue contains a set of mutating methods that observe arrays, so they will also trigger view updates. These methods are as follows:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

You open the console and call the mutant method example1.items.push({message: ‘Baz’}) with the items array from the previous example.

Replace the array

Mutation methods, as the name suggests, change the original array called by these methods. In contrast, there are non-mutating methods, such as filter(), concat() and slice(). These do not change the original array, but always return a new array. When using the non-mutating method, we can replace the old array with the new one:

example1.items = example1.items.filter(function (item) {
  return item.message.match(/Foo/)
})
Copy the code

You might think that this would cause Vue to discard the existing DOM and re-render the entire list. Fortunately, this is not the case. Vue implements some smart, heuristic methods to maximize the reuse of DOM elements, so it is very efficient to replace an array with an array containing the same elements.

Matters needing attention

Due to JavaScript limitations, Vue cannot detect the following altered arrays:

  • When you set an item directly using an index, for example:vm.items[indexOfItem] = newValue
  • When you modify the length of an array, for example:vm.items.length = newLength

To solve the first type of problem, either of the following methods can achieve the same effect as vm.items[indexOfItem] = newValue, and also trigger a status update:

// Vue.set
Vue.set(example1.items, indexOfItem, newValue)
Copy the code
// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)
Copy the code

To solve the second type of problem, you can use Splice:

example1.items.splice(newLength)
Copy the code

Object change detection Precautions

Again, due to JavaScript limitations, Vue cannot detect the addition or deletion of object attributes:

var vm = new Vue({
  data: {
    a: 1}})// 'vm.a' is now responsive

vm.b = 2
// 'vm.b' is not responsive
Copy the code

Vue cannot dynamically add root-level reactive properties to instances that have already been created. However, you can use the vue.set (Object, key, value) method to add responsive properties to nested objects. For example, for:

var vm = new Vue({
  data: {
    userProfile: {
      name: 'Anika'
    }
  }
})
Copy the code

You can add a new age attribute to the nested userProfile object:

Vue.set(vm.userProfile, 'age', 27)
Copy the code

You can also use the vm.$set instance method, which is just an alias for the global vue. set:

vm.$set(this.userProfile, 'age', 27)
Copy the code

Sometimes you may need to assign multiple new attributes to existing objects, such as object.assign () or _.extend(). In this case, you should create a new object with the properties of both objects. So, if you want to add new responsive attributes, don’t look like this:

Object.assign(this.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})
Copy the code

Here’s what you should do:

this.userProfile = Object.assign({}, this.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})
Copy the code

Displays filtering/sorting results

Sometimes we want to display a filtered or sorted copy of an array without actually changing or resetting the original data. In this case, you can create computed properties that return filtered or sorted arrays.

Such as:

<li v-for="n in evenNumbers">{{ n }}</li>
Copy the code
data: {
  numbers: [ 1.2.3.4.5 ]
},
computed: {
  evenNumbers: function () {
    return this.numbers.filter(function (number) {
      return number % 2= = =0}}})Copy the code

In cases where computed attributes do not apply (for example, in nested V-for loops) you can use a method method:

<li v-for="n in even(numbers)">{{ n }}</li>
Copy the code
data: {
  numbers: [ 1.2.3.4.5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2= = =0}}})Copy the code

A range of values of V -for

V minus for can also be integers. In this case, it will repeat the template multiple times.

<div>
  <span v-for="n in 10">{{ n }} </span>
</div>
Copy the code

v-for on a <template>

Similar to v-if, you can render multiple elements with

<ul>
  <template v-for="item in items">
    <li>{{ item.msg }}</li>
    <li class="divider"></li>
  </template>
</ul>
Copy the code

v-for with v-if

When they are on the same node, V-for takes precedence over V-IF, which means that V-IF will run separately in each V-for loop. This priority mechanism is useful when you want to render nodes for only a few items, as follows:

<li v-for="todo in todos" v-if="! todo.isComplete"> {{ todo }} </li>Copy the code

The above code only passes unfinished TODOS.

If your goal is to conditionally skip the loop, place v-if on the outer element (or

<ul v-if="todos.length"> <li v-for="todo in todos"> {{ todo }} </li> </ul> <p v-else>No todos left! </p>Copy the code

One-componentv-for

To learn about components, view components. You can skip it and come back to it later.

In custom components, you can use V-for just like any normal element.

<my-component v-for="item in items" :key="item.id"></my-component>
Copy the code

In version 2.2.0+, keys are now required when using V-for in components.

However, no data is automatically passed to the component because the component has its own independent scope. To pass iteration data to the component, we want to use props:

<my-component
  v-for="(item, index) in items"
  v-bind:item="item"
  v-bind:index="index"
  v-bind:key="item.id"
></my-component>
Copy the code

The reason for not automatically injecting items into the component is that it tightly couples the component to the v-for operation. Knowing where component data comes from enables components to be reused in other contexts.

Here is a complete example of a simple todo list:

<div id="todo-list-example">
  <input
    v-model="newTodoText"
    v-on:keyup.enter="addNewTodo"
    placeholder="Add a todo"
  >
  <ul>
    <li
      is="todo-item"
      v-for="(todo, index) in todos"
      v-bind:key="todo.id"
      v-bind:title="todo.title"
      v-on:remove="todos.splice(index, 1)"
    ></li>
  </ul>
</div>
Copy the code

Notice the is=”todo-item” property here. This is necessary when using DOM templates, because only

  • elements are considered valid within
      elements. Doing so achieves the same effect as

      , but avoids some potential browser parsing errors. See DOM template parsing instructions for more information.
  • Vue.component('todo-item', {
      template: '\
        <li>\
          {{ title }}\
          <button v-on:click="$emit(\'remove\')">X</button>\
        </li>\
      ',
      props: ['title']
    })
    
    new Vue({
      el: '#todo-list-example',
      data: {
        newTodoText: '',
        todos: [
          {
            id: 1,
            title: 'Do the dishes',
          },
          {
            id: 2,
            title: 'Take out the trash',
          },
          {
            id: 3,
            title: 'Mow the lawn'
          }
        ],
        nextTodoId: 4
      },
      methods: {
        addNewTodo: function () {
          this.todos.push({
            id: this.nextTodoId++,
            title: this.newTodoText
          })
          this.newTodoText = ''
        }
      }
    })
    Copy the code