列表渲染

v-for

我们可以使用 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' }
]
}
})

结果

v-for 块内,我们可以完全访问父作用域属性,以及一个特殊的变量 $index,它表示当前项目的数组索引

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

结果

或者,你也可以为索引(或 v-for 用于对象时的键)指定一个别名

<div v-for="(index, item) in items">
{{ index }} {{ item.message }}
</div>

从 1.0.17 版本开始,你也可以使用 of 作为分隔符,而不是 in,使其更接近 JavaScript 迭代器的语法

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

模板 v-for

类似于模板 v-if,你也可以使用带有 v-for<template> 标签来渲染多个元素的块。例如

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

数组变更检测

变异方法

Vue.js 包装了被观察数组的变异方法,以便它们也会触发视图更新。被包装的方法是

你可以在控制台中使用之前的示例的 items 数组,通过调用它的变异方法来进行操作。例如:example1.items.push({ message: 'Baz' })

替换数组

变异方法,顾名思义,会改变它们被调用的原始数组。相比之下,也有一些非变异方法,例如 filter()concat()slice(),它们不会改变原始数组,而是 **始终返回一个新的数组**。在使用非变异方法时,你可以用新数组替换旧数组

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

你可能会认为这会导致 Vue.js 丢弃现有的 DOM 并重新渲染整个列表 - 幸运的是,情况并非如此。Vue.js 实现了一些智能启发式算法,以最大限度地重用 DOM 元素,因此用包含重叠对象的另一个数组替换数组是一个非常高效的操作。

track-by

在某些情况下,你可能需要用完全新的对象替换数组 - 例如,从 API 调用创建的对象。由于默认情况下 v-for 通过跟踪其数据对象的标识来确定现有作用域和 DOM 元素的可重用性,这会导致整个列表被重新渲染。但是,如果你的每个数据对象都有一个唯一的 id 属性,那么你可以使用 track-by 特殊属性来提示 Vue.js,以便它可以尽可能地重用现有实例。

例如,如果你的数据如下所示

{
items: [
{ _uid: '88f869d', ... },
{ _uid: '7496c10', ... }
]
}

那么你可以这样给出提示

<div v-for="item in items" track-by="_uid">
<!-- content -->
</div>

稍后,当你替换 items 数组时,Vue.js 遇到一个新的对象,其 _uid: '88f869d',它知道可以重用与相同 _uid 关联的现有作用域和 DOM 元素。

track-by $index

如果你没有唯一的键来跟踪,你也可以使用 track-by="$index",这将强制 v-for 进入就地更新模式:片段不再移动,它们只是用对应索引的新值刷新。此模式也可以处理源数组中的重复值。

这可以使数组替换非常高效,但它也有一些权衡。由于 DOM 节点不再移动以反映顺序的变化,因此 DOM 输入值和组件私有状态等临时状态可能会不同步。因此,在使用 track-by="$index" 时要小心,如果 v-for 块包含表单输入元素或子组件。

注意事项

由于 JavaScript 的限制,Vue.js **无法** 检测到对数组的以下更改

  1. 当你直接用索引设置一个项目时,例如 vm.items[0] = {}
  2. 当你修改数组的长度时,例如 vm.items.length = 0

为了处理注意事项 (1),Vue.js 为被观察数组添加了一个 $set() 方法

// same as `example1.items[0] = ...` but triggers view update
example1.items.$set(0, { childMsg: 'Changed!'})

为了处理注意事项 (2),只需用一个空数组替换 items 即可。

除了 $set() 之外,Vue.js 还为数组添加了一个便捷方法 $remove(),它通过内部调用 splice() 来搜索并从目标数组中删除一个项目。因此,与其

var index = this.items.indexOf(item)
if (index !== -1) {
this.items.splice(index, 1)
}

你可以直接做

this.items.$remove(item)

使用 Object.freeze()

当迭代一个用 Object.freeze() 冻结的对象数组时,你需要显式地使用 track-by 键。当 Vue.js 无法自动跟踪对象时,在这种情况下会显示一个警告。

对象 v-for

你也可以使用 v-for 来迭代对象的属性。除了 $index 之外,每个作用域都可以访问另一个特殊属性 $key

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

结果

你也可以为键提供一个别名

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

当迭代一个对象时,顺序基于 Object.keys() 的键枚举顺序,这 **不** 保证在所有 JavaScript 引擎实现中都是一致的。

范围 v-for

v-for 也可以接受一个整数。在这种情况下,它将重复该模板多次。

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

结果

{{ n }}

显示过滤/排序的结果

有时我们只需要显示数组的过滤或排序版本,而不需要实际改变或重置原始数据。有两种方法可以实现这一点

  1. 创建一个返回过滤或排序数组的计算属性;
  2. 使用内置的 filterByorderBy 过滤器。

计算属性将为你提供更细粒度的控制和更大的灵活性,因为它完全是 JavaScript;但过滤器对于常见用例来说可能更方便。有关数组过滤器的详细用法,请查看它们的 文档