List Rendering 看這名字~難道是上一集提到的v-for ?
好像是耶!用迴圈把列表陣列裡頭的內容渲染出來~
框架把html標籤裡頭應用基本程式語法~也許應該連繼承都有?
v-for
指令用來基於陣列渲染項目列表。其語法為 item in items
,其中 items
是數據來源的陣列,item
是當前迭代的元素的別名:
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
<li v-for="item in items">
{{ item.message }}
</li>
在 v-for
的範圍內,模板表達式可以訪問所有父範圍的屬性
。此外,v-for
也支持一個可選的第二個別名來表示當前項目的索引
:
const parentMessage = ref('Parent')
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
v-for
的變數範圍與 JavaScript 的 forEach
方法類似:
const parentMessage = 'Parent';
const items = [
/* ... */
];
items.forEach((item, index) => {
// 可以訪問外部範圍 `parentMessage`
// 但 `item` 和 `index` 僅在這裡可用
console.log(parentMessage, item.message, index);
});
這段代碼展示了 v-for
的變數範圍如何與 forEach
回調函數的簽名相匹配。你可以像對待函數參數一樣使用解構賦值來簡化 v-for
的項目別名:
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- 使用索引別名 -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>
對於嵌套的 v-for
,範圍的工作方式也類似於嵌套函數。每個 v-for
範圍都可以訪問其父範圍:
<li v-for="item in items">
<span v-for="childItem in item.children">
{{ item.message }} {{ childItem }}
</span>
</li>
你也可以使用 of
代替 in
,這樣語法更接近 JavaScript 的迭代器語法:
<div v-for="item of items"></div>
你也可以使用 v-for
來遍歷物件的屬性。遍歷的順序將基於對物件調用 Object.values()
的結果:
const myObject = reactive({
title: 'How to do lists in Vue',
author: 'Jane Doe',
publishedAt: '2016-04-10'
});
<ul>
<li v-for="value in myObject">
{{ value }}
</li>
</ul>
這段代碼會顯示物件 myObject
中所有屬性的值。你也可以提供第二個別名來表示屬性的名稱(也叫做 key):
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
你還可以提供第三個別名來表示索引:
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
這樣,v-for
可以遍歷物件的屬性及其值,並且可以選擇性地顯示屬性的名稱和索引。
v-for
with a Rangev-for
也可以接受一個整數。在這種情況下,它會根據範圍 1...n
重複模板多次。
<span v-for="n in 10">{{ n }}</span>
注意這裡 n
從初始值 1 開始,而不是 0。
v-for
on <template>
類似於 v-if
,也可在 <template>
標籤上使用 v-for
來渲染多個元素區塊。
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for
with v-if
注意: 不建議在同一個元素上同時使用 v-if
和 v-for
,因為它們之間存在隱式的優先順序。詳細資訊請參見風格指南。(這個講第二次了www)
當 v-if
和 v-for
同時存在於同一個節點上時,v-if
的優先級高於 v-for
。這意味著 v-if
條件無法訪問 v-for
的範圍內的變數:
<!--
這會拋出錯誤,因為屬性 "todo" 在實例中未定義。
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
通過將 v-for
移到 <template>
標籤包裝起來修正這個問題(這樣也更為明確):
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
key
維護狀態 - Maintaining State with key
當 Vue 更新使用 v-for
渲染的元素列表時,預設情況下,它使用 "就地補丁"(in-place patch) 策略。如果資料項目的順序發生了變化,Vue 會在原地補丁每個元素,確保它反映當前應該渲染的內容,而不是移動 DOM 元素以匹配項目的順序。
這種預設模式效率很高,但僅適用於列表渲染輸出不依賴於子組件狀態或臨時 DOM 狀態(例如表單輸入值)的情況。
為了讓 Vue 能夠跟蹤每個節點的身份
,從而重用和重新排序現有元素,你需要為每個項目提供一個唯一的 key
屬性:
<div v-for="item in items" :key="item.id">
<!-- 內容 -->
</div>
當使用 <template v-for>
時,key
應該放在 <template>
容器上:
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
注意: 這裡的 key
是一個特殊的屬性,用於綁定 v-bind
。它不應與在使用 v-for
遍歷物件時的屬性 key
變數混淆。
建議在使用 v-for
時提供 key
屬性,除非迭代的 DOM 內容很簡單(即不包含組件或有狀態的 DOM 元素),或你有意依賴預設行為以獲得性能提升。
key
綁定應該使用基本值,如字符串和數字。不要使用物件作為 v-for
的 key
。有關 key
屬性的詳細使用,請參見 key API 文檔。
v-for
with a Component本節假設你對組件已有了解。如果不熟悉,建議稍後再回來閱讀。
你可以像使用普通元素一樣,直接在組件上使用 v-for
(別忘了提供 key
):
<MyComponent v-for="item in items" :key="item.id" />
然而,這樣做不會自動將任何數據傳遞給組件,因為組件具有獨立的作用域。為了將迭代數據傳遞給組件
,我們還需要使用 props
:
<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()
,這些方法不會修改原始陣列,而是返回一個新陣列。當使用這些非變異方法時,我們應該用新陣列替換舊陣列:
// `items` 是一個具有陣列值的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))
你可能會擔心這會導致 Vue 丟棄現有的 DOM 並重新渲染整個列表,但實際上並非如此。Vue 實施了智能的啟發式算法以最大化 DOM 元素的重用,因此用另一個包含重疊對象的陣列替換陣列是一個非常高效的操作。
有時我們希望顯示過濾或排序後的陣列版本,而不實際修改或重置原始數據。在這種情況下,可以創建一個計算屬性來返回過濾或排序後的陣列。
const numbers = ref([1, 2, 3, 4, 5])
const evenNumbers = computed(() => {
return numbers.value.filter((n) => n % 2 === 0)
})
在這裡,我們有一個 numbers
陣列,並使用 computed
來創建一個新的計算屬性 evenNumbers
。這個計算屬性會返回所有偶數。模板可以這樣顯示:
<li v-for="n in evenNumbers">{{ n }}</li>
如果計算屬性不適用(例如在多層嵌套的 v-for
中),可使用方法來過濾或排序數據。
const sets = ref([
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10]
])
function even(numbers) {
return numbers.filter((number) => number % 2 === 0)
}
在這裡,我們有一個 sets
陣列,包含多個數字陣列。我們定義了一個方法 even
,用來過濾出偶數。在模板中,可以這樣使用:
<ul v-for="numbers in sets">
<li v-for="n in even(numbers)">{{ n }}</li>
</ul>
使用 reverse()
和 sort()
方法時要小心,因為這些方法會修改原始陣列。為了避免計算屬性中直接修改原始數據,應先創建原始數組的副本,再進行排序或反轉操作。
// 不建議的做法
// return numbers.reverse()
// 推薦的做法 創建原始數組的副本,再進行操作
return [...numbers].reverse()
...numbers
:這是展開運算符(spread operator),用來將 numbers
陣列中的所有元素展開成單獨的元素。例如,如果 numbers
是 [1, 2, 3]
,那麼 ...numbers
就相當於 1, 2, 3
。
[...numbers]
:這裡用展開運算符將 numbers
陣列中的元素展開,然後包裹在新的陣列中,從而創建一個新的副本,而不改變原始的 numbers
陣列。這意味著你現在有了一個與 numbers
內容相同但獨立於它的新的陣列。
持續學習各種框架特性!
但會有一些例外~什麼東西不能跟什麼東西一起用~
原因是什麼理由是什麼~還需要好好內化吸收一下www
一起來吸收~~~