列表渲染
v-for
我們可以使用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>
在`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>
`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
console.log(parentMessage, item.message, index)
})
注意`v-for`的值如何與`forEach`回撥函式的函式簽名匹配。實際上,你可以在`v-for`項別名上使用類似解構函式引數的解構
template
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- with index alias -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>
巢狀`v-for`的作用域也類似於巢狀函式。每個`v-for`作用域都可以訪問父作用域
template
<li v-for="item in items">
<span v-for="childItem in item.children">
{{ item.message }} {{ childItem }}
</span>
</li>
你還可以使用`of`作為分隔符而不是`in`,這樣它就更接近JavaScript的迭代器語法
template
<div v-for="item of items"></div>
v-for
與物件
您還可以使用 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>
您還可以提供第二個別名來表示屬性名(即鍵)
template
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
另一個用於索引
template
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
v-for
與範圍
v-for
也可以接受一個整數。在這種情況下,它將根據範圍 1...n
重複模板多次。
template
<span v-for="n in 10">{{ n }}</span>
注意這裡 n
的初始值為 1
,而不是 0
。
v-for
在 <template>
上
與模板 v-if
類似,您也可以使用帶有 v-for
的 <template>
標籤來渲染多個元素的塊。例如
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-if
和 v-for
。有關詳細資訊,請參閱 風格指南。
當它們存在於同一節點時,v-if
的優先順序高於 v-for
。這意味著 v-if
條件將無法訪問 v-for
範圍的變數
template
<!--
This will throw an error because property "todo"
is not defined on instance.
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
可以透過將 v-for
移動到包裝的 <template>
標籤來解決此問題(這也更明確)
template
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
使用 key
維護狀態
當 Vue 更新使用 v-for
渲染的元素列表時,預設情況下它使用“原地修補”策略。如果資料項的順序已更改,Vue 不會移動 DOM 元素以匹配專案的順序,而是會原地修補每個元素並確保它反映了特定索引應該渲染的內容。
此預設模式是高效的,但**僅適用於您的列表渲染輸出不依賴於子元件狀態或臨時 DOM 狀態(例如表單輸入值)的情況**。
要給 Vue 提供提示,使其能夠跟蹤每個節點的身份,從而重用和重新排序現有元素,您需要為每個專案提供唯一的 key
屬性
template
<div v-for="item in items" :key="item.id">
<!-- content -->
</div>
當使用 <template v-for>
時,key
應放置在 <template>
容器上
template
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
注意
這裡的 key
是一個與 v-bind
繫結的特殊屬性。當使用 v-for
與物件 時,不要將其與屬性鍵變數混淆。
建議在可能的情況下為 v-for
提供一個 key
屬性,除非迭代 DOM 內容很簡單(即不包含元件或具有狀態性的 DOM 元素),或者您有意依賴預設行為以獲得性能提升。
key
繫結期望原始值 - 即字串和數字。不要將物件用作 v-for
鍵。有關 key
屬性的詳細使用方法,請參閱 key
API 文件。
v-for
與元件
本節假設您已瞭解 元件。您可以隨時跳過此部分,稍後再回來。
您可以直接在元件上使用 v-for
,就像任何普通元素一樣(別忘了提供 key
)。
template
<MyComponent v-for="item in items" :key="item.id" />
但是,這不會自動將任何資料傳遞給元件,因為元件有自己的獨立作用域。為了將迭代的傳給元件,我們還應該使用 props。
template
<MyComponent
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
/>
不自動將 item
注入元件的原因是因為這會使元件與 v-for
的工作方式緊密耦合。明確其資料來源使元件可以在其他情況下重用。
檢視 這個簡單的待辦事項列表示例,瞭解如何使用 v-for
渲染元件列表,併為每個例項傳遞不同的資料。
陣列變更檢測
變異方法
Vue 能夠檢測到呼叫響應式陣列的變異方法,並觸發必要的更新。這些變異方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
替換陣列
變異方法,顧名思義,會改變它們被呼叫的原始陣列。相比之下,也存在非變異方法,例如 filter()
、concat()
和 slice()
,它們不會改變原始陣列,但總是返回一個新陣列。當使用非變異方法時,我們應該用新陣列替換舊陣列。
js
// `items` is a ref with array value
items.value = items.value.filter((item) => item.message.match(/Foo/))
您可能認為這將導致 Vue 丟棄現有的 DOM 並重新渲染整個列表——幸運的是,並非如此。Vue 實現了一些智慧啟發式方法來最大化 DOM 元素的複用,因此用包含重疊物件的另一個數組替換陣列是一個非常高效的操作。
顯示過濾/排序結果
有時我們想顯示陣列的過濾或排序版本,而不實際修改或重置原始資料。在這種情況下,您可以建立一個計算屬性,該屬性返回過濾或排序後的陣列。
例如
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>
在計算屬性不可行的情況下(例如,在巢狀 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>
小心使用計算屬性中的 reverse()
和 sort()
!這兩個方法會修改原始陣列,這應該避免在計算屬性中。在這些方法之前建立原始陣列的副本。
diff
- return numbers.reverse()
+ return [...numbers].reverse()