List rendering


Directory


Use v-for to correspond an array to a set of elements


We can use ## The #v-for directive renders a list based on an array. The v-for directive requires special syntax of the form item in items, where items is the source data array and item is the target The alias of the array element iterated over.

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

Result:

1.jpg

In the

v-for block, we can access all the properties of the parent scope. v-for also supports an optional second parameter, the index of the current item.

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

Result:

2.jpg

You can also use

of instead of in as the delimiter because it is more Syntax close to JavaScript iterators:

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


Using objects in v-for


You can also use

v-for to traverse the properties of an object.

<ul id="v-for-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
new Vue({
  el: '#v-for-object',
  data: {
    object: {
      title: 'How to do lists in Vue',
      author: 'Jane Doe',
      publishedAt: '2016-04-10'
    }
  }
})

Result:

3.jpg

You can also provide the second parameter as the property name (that is, the key name):

<div v-for="(value, name) in object">
  {{ name }}: {{ value }}
</div>

4.jpg

You can also use the third parameter as the index:

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

5.jpg

When traversing the object, you will press Object.keys() The results are traversed, but cannot guarantee that its results will be consistent under different JavaScript engines.


Maintenance status


When Vue is updating use v -for When rendering a list of elements, it defaults to using the "update 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 update each element in place and ensure that they render correctly at each index position. This is similar to Vue 1.x’s track-by="$index".

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

To give Vue a hint so that it can keep track of the identity of each node and thus reuse and reorder existing elements, you need to provide a unique key attribute for each item:

<div v-for="item in items" v-bind:key="item.id">
  <!-- 内容 -->
</div>

It is recommended to provide the key attribute when using v-for whenever possible, unless traversing the output DOM content is very simple, or deliberately relying on the default behavior to gain performance promote.

Because it is a general mechanism for Vue to identify nodes, key is not specifically related only to v-for. As we'll see later in the guide, it also has other uses.

Do not use non-basic type values ​​such as objects or arrays as the key of v-for. Please use a string or numeric value.

For more detailed usage of key attribute, please go to the API documentation of key.


Array update detection



##Mutation method

Vue wraps the mutation methods of the listened array, so they will also trigger view updates. These wrapped methods include:

  • push()

  • pop()

  • shift()

  • ##unshift()

  • splice()

  • sort()

  • reverse ()

  • You can open the console and try to call the mutation method on the
items

array in the previous example. For example example1.items.push({ message: 'Baz' }).


Replace array

Mutation methods, as the name suggests, will change the original array on which these methods are called. In contrast, there are also non-mutating methods, such as filter(), concat() and slice(). They do not alter the original array, and always return a new array . When using the non-mutating approach, you can replace the old array with the new array:

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

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 heuristics in order to maximize the reuse of DOM elements, so replacing the original array with an array containing the same elements is a very efficient operation.


Notes

Due to JavaScript limitations, Vue cannot detect the following Changes in the array:

  1. When you use the index to directly set an array item, for example: vm.items[indexOfItem] = newValue

  2. When you modify the length of the array, for example: vm.items.length = newLength

For example:

var vm = new Vue({
  data: {
    items: ['a', 'b', 'c']
  }
})
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的

In order to solve the first type of problem, the following two methods can achieve the same effect as vm.items[indexOfItem] = newValue, and will also trigger status updates in the responsive system:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

You can also use the vm.$set instance method, which is an alias of the global method Vue.set:

vm.$set(vm.items, indexOfItem, newValue)

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

vm.items.splice(newLength)


##Object change detection considerations


Or due to JavaScript limitations,

Vue cannot detect the addition or deletion of object properties:

var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 现在是响应式的
vm.b = 2
// `vm.b` 不是响应式的

For already created instances, Vue does not allow dynamics Add root-level responsive properties. However, you can add reactive properties to nested objects using the

Vue.set(object, propertyName, value) method. For example, for:

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

you can add a new

age attribute to the nested userProfile object:

Vue.set(vm.userProfile, 'age', 27)

You can also use

vm.$set Instance method, it is just an alias of the global Vue.set:

vm.$set(vm.userProfile, 'age', 27)

Sometimes you may need to assign multiple new properties to an existing object, such as using

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 a new responsive attribute, instead of like this:

Object.assign(vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})

you should do this:

vm.userProfile = Object.assign({}, vm.userProfile, {
  age: 27,
  favoriteColor: 'Vue Green'
})


Display filtered/sorted results


Sometimes we want to display a filtered or sorted version of an array without actually Change or reset original data. In this case, you can create a computed property that returns a filtered or sorted array.

For example:

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

In cases where computed properties are not applicable (for example, in a nested v-for loop) you can use a method:

<li v-for="n in even(numbers)">{{ n }}</li>
data: {
  numbers: [ 1, 2, 3, 4, 5 ]
},
methods: {
  even: function (numbers) {
    return numbers.filter(function (number) {
      return number % 2 === 0
    })
  }
}


Use value ranges in v-for


v-for also accepts integers. In this case, it will repeat the template the corresponding number of times.

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

Result:

1.jpg


##at<template> Using v-for

is similar to
v-if

, you can also use v-for 's<template> to loop through rendering a piece of content containing multiple elements. For example:

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


v-for is used together with v-if


Note that we
do not

recommend using v-if and v-for on the same element. More details can be found in the Style Guide.

When they are on the same node,
v-for

has a higher priority than v-if, which means v-if will be run repeatedly in each v-for loop. This priority mechanism is useful when you only want to render nodes for some items, as follows:

<li v-for="todo in todos" v-if="!todo.isComplete">
  {{ todo }}
</li>
The above code will only render unfinished todos.

And if your purpose is to conditionally skip the execution of the loop, then you can place

v-if

in the outer element (or <template> )superior. For example:

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


Use v-for# on the component


##This part assumes that you already have knowledge about

components. You can also skip it and come back to it later. On a custom component, you can use

v-for
just like you would on any normal element.

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

As of version 2.2.0,
key

is now required when using v-for on a component.

However, no data will be automatically passed to the component because the component has its own independent scope. In order to pass the iteration data to the component, we need to use prop:

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

The reason why item is not automatically injected into the component is that this will make the component different from v-for 's operations are tightly coupled. Clarifying the source of a component's data enables the component to be reused in other situations.

Here is a complete example of a simple todo list:

<div id="todo-list-example">
  <form v-on:submit.prevent="addNewTodo">
    <label for="new-todo">Add a todo</label>
    <input
      v-model="newTodoText"
      id="new-todo"
      placeholder="E.g. Feed the cat"
    >
    <button>Add</button>
  </form>
  <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>

Note the is="todo-item" attribute here. This approach is very necessary when using DOM templates, because only the <li> element within the <ul> element will be regarded as valid content. This achieves the same effect as <todo-item>, but can avoid 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\')">Remove</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 = ''
    }
  }
})

1.gif