列表渲染 | List Rendering
v-for
We can use the v-for
directive to render a list of items based on an array. The v-for
directive requires a special syntax in the form of item in items
, where items
is the source data array and item
is an alias for the array element being iterated on:
我们可以使用 v-for
指令基于一个数组来渲染一个列表。v-for
指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而 item
是迭代项的别名:
js
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
template
<li v-for="item in items">
{{ item.message }}
</li>
Inside the v-for
scope, template expressions have access to all parent scope properties. In addition, v-for
also supports an optional second alias for the index of the current item:
在 v-for
块中可以完整地访问父作用域内的属性和变量。v-for
也支持使用可选的第二个参数表示当前项的位置索引。
js
const parentMessage = ref('Parent')
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
template
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
The variable scoping of v-for
is similar to the following JavaScript:
v-for
变量的作用域和下面的 JavaScript 代码很类似:
js
const parentMessage = 'Parent'
const items = [
/* ... */
]
items.forEach((item, index) => {
// has access to outer scope `parentMessage`
// but `item` and `index` are only available in here
// 可以访问外层的 `parentMessage`
// 而 `item` 和 `index` 只在这个作用域可用
console.log(parentMessage, item.message, index)
})
Notice how the v-for
value matches the function signature of the forEach
callback. In fact, you can use destructuring on the v-for
item alias similar to destructuring function arguments: 注意 v-for
是如何对应 forEach
回调的函数签名的。实际上,你也可以在定义 v-for
的变量别名时使用解构,和解构函数参数类似:
template
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- with index alias -->
<!-- 有 index 索引时 -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>
For nested v-for
, scoping also works similar to nested functions. Each v-for
scope has access to parent scopes: 对于多层嵌套的 v-for
,作用域的工作方式和函数的作用域很类似。每个 v-for
作用域都可以访问到父级作用域:
template
<li v-for="item in items">
<span v-for="childItem in item.children">
{{ item.message }} {{ childItem }}
</span>
</li>
You can also use of
as the delimiter instead of in
, so that it is closer to JavaScript's syntax for iterators: 你也可以使用 of
作为分隔符来替代 in
,这更接近 JavaScript 的迭代器语法:
template
<div v-for="item of items"></div>
v-for
与对象 | v-for
with an Object
You can also use v-for
to iterate through the properties of an object. The iteration order will be based on the result of calling Object.values()
on the object:
你也可以使用 v-for
来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.values()
的返回值来决定。
js
const myObject = reactive({
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
})
template
<ul>
<li v-for="value in myObject">
{{ value }}
</li>
</ul>
You can also provide a second alias for the property's name (a.k.a. key):
可以通过提供第二个参数表示属性名 (例如 key):
template
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
And another for the index:
第三个参数表示位置索引:
template
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
在 v-for
里使用范围值 | v-for
with a Range
v-for
can also take an integer. In this case it will repeat the template that many times, based on a range of 1...n
.
v-for
可以直接接受一个整数值。在这种用例中,会将该模板基于 1...n
的取值范围重复多次。
template
<span v-for="n in 10">{{ n }}</span>
Note here n
starts with an initial value of 1
instead of 0
.
注意此处 n
的初值是从 1
开始而非 0
。
<template>
上的 v-for
| v-for
on <template>
Similar to template v-if
, you can also use a <template>
tag with v-for
to render a block of multiple elements. For example:
与模板上的 v-if
类似,你也可以在 <template>
标签上使用 v-for
来渲染一个包含多个元素的块。例如:
template
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for
与 v-if
| v-for
with v-if
Note | 注意
It's not recommended to use v-if
and v-for
on the same element due to implicit precedence. Refer to style guide for details.
同时使用 v-if
和 v-for
是不推荐的,因为这样二者的优先级不明显。请转阅风格指南查看更多细节。
When they exist on the same node, v-if
has a higher priority than v-for
. That means the v-if
condition will not have access to variables from the scope of the v-for
:
当它们同时存在于一个节点上时,v-if
比 v-for
的优先级更高。这意味着 v-if
的条件将无法访问到 v-for
作用域内定义的变量别名:
template
<!--
This will throw an error because property "todo"
is not defined on instance.
-->
<!--
这会抛出一个错误,因为属性 todo 此时
没有在该实例上定义
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
This can be fixed by moving v-for
to a wrapping <template>
tag (which is also more explicit):
在外先包装一层 <template>
再在其上使用 v-for
可以解决这个问题 (这也更加明显易读):
template
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
通过 key 管理状态 | Maintaining State with key
When Vue is updating a list of elements rendered with v-for
, by default it uses an "in-place patch" strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will patch each element in-place and make sure it reflects what should be rendered at that particular index.
Vue 默认按照“就地更新”的策略来更新通过 v-for
渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM 元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
This default mode is efficient, but only suitable when your list render output does not rely on child component state or temporary DOM state (e.g. form input values).
默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况。
To give Vue a hint so that it can track each node's identity, and thus reuse and reorder existing elements, you need to provide a unique key
attribute for each item:
为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key
attribute:
template
<div v-for="item in items" :key="item.id">
<!-- content -->
<!-- 内容 -->
</div>
When using <template v-for>
, the key
should be placed on the <template>
container:
当你使用 <template v-for>
时,key
应该被放置在这个 <template>
容器上:
template
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
| 注意
key
here is a special attribute being bound with v-bind
. It should not be confused with the property key variable when using v-for
with an object.
key
在这里是一个通过 v-bind
绑定的特殊 attribute。请不要和在 v-for
中使用对象里所提到的对象属性名相混淆。
It is recommended to provide a key
attribute with v-for
whenever possible, unless the iterated DOM content is simple (i.e. contains no components or stateful DOM elements), or you are intentionally relying on the default behavior for performance gains.
推荐在任何可行的时候为 v-for
提供一个 key
attribute,除非所迭代的 DOM 内容非常简单 (例如:不包含组件或有状态的 DOM 元素),或者你想有意采用默认行为来提高性能。
The key
binding expects primitive values - i.e. strings and numbers. Do not use objects as v-for
keys. For detailed usage of the key
attribute, please see the key
API documentation.
key
绑定的值期望是一个基础类型的值,例如字符串或 number 类型。不要用对象作为 v-for
的 key。关于 key
attribute 的更多用途细节,请参阅 key
API 文档。
组件上使用 v-for
| v-for
with a Component
This section assumes knowledge of Components. Feel free to skip it and come back later.
这一小节假设你已了解组件的相关知识,或者你也可以先跳过这里,之后再回来看。
You can directly use v-for
on a component, like any normal element (don't forget to provide a key
):
我们可以直接在组件上使用 v-for
,和在一般的元素上使用没有区别 (别忘记提供一个 key
):
template
<MyComponent v-for="item in items" :key="item.id" />
However, this won't automatically pass any data to the component, because components have isolated scopes of their own. In order to pass the iterated data into the component, we should also use props:
但是,这不会自动将任何数据传递给组件,因为组件有自己独立的作用域。为了将迭代后的数据传递到组件中,我们还需要传递 props:
template
<MyComponent
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
/>
The reason for not automatically injecting item
into the component is because that makes the component tightly coupled to how v-for
works. Being explicit about where its data comes from makes the component reusable in other situations.
不自动将 item
注入组件的原因是,这会使组件与 v-for
的工作方式紧密耦合。明确其数据的来源可以使组件在其他情况下重用。
Check out this example of a simple todo list to see how to render a list of components using v-for
, passing different data to each instance.
这里是一个简单的 Todo List 的例子,展示了如何通过 v-for
来渲染一个组件列表,并向每个实例中传入不同的数据。
数组变化侦测 | Array Change Detection
变更方法 | Mutation Methods
Vue is able to detect when a reactive array's mutation methods are called and trigger necessary updates. These mutation methods are:
Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
替换一个数组 | Replacing an Array
Mutation methods, as the name suggests, mutate the original array they are called on. In comparison, there are also non-mutating methods, e.g. filter()
, concat()
and slice()
, which do not mutate the original array but always return a new array. When working with non-mutating methods, we should replace the old array with the new one:
变更方法,顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一些不可变 (immutable) 方法,例如 filter()
,concat()
和 slice()
,这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的:
js
// `items` is a ref with array value
// `items` 是一个数组的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))
You might think this will cause Vue to throw away the existing DOM and re-render the entire list - luckily, that is not the case. Vue implements some smart heuristics to maximize DOM element reuse, so replacing an array with another array containing overlapping objects is a very efficient operation.
你可能认为这将导致 Vue 丢弃现有的 DOM 并重新渲染整个列表——幸运的是,情况并非如此。Vue 实现了一些巧妙的方法来最大化对 DOM 元素的重用,因此用另一个包含部分重叠对象的数组来做替换,仍会是一种非常高效的操作。
展示过滤或排序后的结果 | Displaying Filtered/Sorted Results
Sometimes we want to display a filtered or sorted version of an array without actually mutating or resetting the original data. In this case, you can create a computed property that returns the filtered or sorted array.
有时,我们希望显示数组经过过滤或排序后的内容,而不实际变更或重置原始数据。在这种情况下,你可以创建返回已过滤或已排序数组的计算属性。
For example:
举例来说:
js
const numbers = ref([1, 2, 3, 4, 5])
const evenNumbers = computed(() => {
return numbers.value.filter((n) => n % 2 === 0)
})
template
<li v-for="n in evenNumbers">{{ n }}</li>
In situations where computed properties are not feasible (e.g. inside nested v-for
loops), you can use a method:
在计算属性不可行的情况下 (例如在多层嵌套的 v-for
循环中),你可以使用以下方法:
js
const sets = ref([
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]
])
function even(numbers) {
return numbers.filter((number) => number % 2 === 0)
}
template
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)">{{ n }}</li>
</ul>
Be careful with reverse()
and sort()
in a computed property! These two methods will mutate the original array, which should be avoided in computed getters. Create a copy of the original array before calling these methods:
在计算属性中使用 reverse()
和 sort()
的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:
diff
- return numbers.reverse()
+ return [...numbers].reverse()