vue学习---列表渲染

in Vue教程 with 0 comments, 4675 views

列表渲染

v-for

我们可以使用 v-for 指令基于一个数组渲染一个列表。这个指令使用特殊的语法,形式为 item in items,items 是数据数组,item 是当前数组元素的别名:

基本用法

<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' }
    ]
  }
})

在线DEMO

在v-for块内部,我们可以完全访问父级作用域的属性,并且支持可选的第二个参数来标记当前元素的索引值(Vue2.x的更新,原有的$index已经移除)。

<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' }
    ]
  }
})

在线DEMO

你也可以使用of来代替定界符in,这样看起来更像ES6提供的迭代器语法.

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

Template v-for

类似于 template v-if,也可以将 v-for 用在 <template> 标签上,以渲染一个包含多个元素的块,最终渲染的结果中并不会包含template标签。例如:

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

Object v-for

你也可用使用v-for来迭代(遍历)一个对象的属性.

<ul id="repeat-object" class="demo">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
new Vue({
  el: '#repeat-object',
  data: {
    object: {
      FirstName: 'John',
      LastName: 'Doe',
      Age: 30
    }
  }
})

在线DEMO

同样的我们可以添加第二个参数来标识这些key:

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

再加上index参数:

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

注意:在遍历对象时,是按 Object.keys() 的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下是一致的。

Range v-for(值域)

v-for也可以处理一个整数,在这种情况下模板会循环多次。

<div id="repeat-n">
  <span v-for="n in 10">{{ n }}</span>
</div>
new Vue({
  el: '#repeat-n'
})

在线DEMO

Components and v-for

我们也可以循环一个组件(组件概念会在后面篇章中介绍),你可以像其它普通元素一样直接在一个自定义组件上使用v-for指令.

<my-component v-for="item in items"></my-component>

不过,这并不会自动的传递任何数据给组件,因为组件有它自己独立的作用域,所以为了传递迭代数据给组件,我们需要一点点额外步骤(借助props)

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

其实不将数据(上面例子中的item)自动注入到组件中是有它的理由的,因为那样的话会造成组件和v-for的强耦合,明确了数据来源后会让组件在其它场景中更好的被复用.

下面是一个简单的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:title="todo"
      v-on:remove="todos.splice(index, 1)"
    ></li>
  </ul>
</div>
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: [
      'Do the dishes',
      'Take out the trash',
      'Mow the lawn'
    ]
  },
  methods: {
    addNewTodo: function () {
      this.todos.push(this.newTodoText)
      this.newTodoText = ''
    }
  }
})

注意: 例子中使用了is指令来标识li用自定义组件'todo-item'来解析渲染 在线DEMO

key

当VUE更新一个利用v-for指令渲染的元素列表时,vue默认采用的是"原位更新"(in-place patch)的策略. 如果列表的数据项发生改变,vue并不会通过移除DOM元素来重新匹配列表数据项的顺序,而是简单的保持原位刷新(更新索引值)并且确保映射了以唯一索引值来进行渲染。这个和Vue 1.x中的track-by="$index"的形为类似。

这样的默认处理策略是高效的,不过只适用于你的列表渲染不依赖于子组件的状态或者临时DOM的状态(e.g input表单输入)

😀当然也可以自定义策略,我们需要给Vue一点提示,这样Vue才能懂你的心思,我们需要为每一个列表项提供一个唯一的key属性,比如id便是一个理想的值,因为一般来说id是唯一的嘛。

这个特殊的key属性和Vue 1.x中的track-by="$index"大致相同,不过它既然是属性,就得以属性的方式作业,所以我们得使用v-bind指令来绑定变量(😅嫌v-bind啰嗦可以使用简写):

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

官方推荐无论何时都需要提供一个key属性,除非数据项比较简单不会发生重排的情况,或者你确实就需要默认策略的那种效果来展示.

由于这是Vue用来标识唯一结点的通用机制,key还有很多其它用途😄日后再说。

数组变化检测(Array Change Detection)

变异方法(Mutation Methods)

Vue.js 包装了被观察数组的变异方法,故它们能触发视图更新。被包装的方法有:

你可以打开浏览器的控制台,用这些方法修改上例的 items 数组。例如:example1.items.push({ message: 'Baz' })

替换数组(Replacing an Array)

变异方法,如名字所示,修改了原始数组。相比之下,也有非变异方法,如 filter(), concat() 和 slice(),不会修改原始数组而是返回一个新数组。在使用非变异方法时,可以直接用新数组替换旧数组:

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

可能你觉得这将导致 Vue.js 弃用已有 DOM 并重新渲染整个列表——幸运的是并非如此。 Vue.js 实现了一些启发算法,以最大化复用 DOM 元素,因而用另一个数组替换数组是一个非常高效的操作。

Caveats(警告)

因为 JavaScript 的限制,Vue.js 不能检测到下面数组变化(不会触发视图更新):

  1. 直接用索引设置元素,e.g.vm.items[indexOfItem] = newValue
  2. 修改数据的长度, e.g. vm.items.length = newLength

为了解决问题 (1),Vue.js 扩展了观察数组,为它添加了一个 Vue.set 方法(原来Vue 1.x中是vm.$set):

// Vue.set
Vue.set(example1.items, indexOfItem, newValue)

或者使用splice:

// Array.prototype.splice`
example1.items.splice(indexOfItem, 1, newValue)

至于问题 (2),我们还是用到splice(vm.$remove在Vue 2.x中已经移除):

example1.items.splice(newLength)

显示过滤/排序的结果(Displaying Filtered/Sorted Results)

有时我们想显示过滤/排序过的数组,同时不实际修改或重置原始数据。这种情况下,我们可以创建一个计算属性用来返回过滤/排序过的数组。

例如:

<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
    })
  }
}

作为另一种选择,我们可以在自定义属性不适用的时候(前面提到过计算属性是惰性的)使用一个方法代替(e.g. 内部嵌套的v-for循环):

<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
    })
  }
}
Responses ${replyToWho} / Cancel Reply