Appearance
Rendering danh sách
v-for
Chúng ta có thể sử dụng chỉ thị v-for
để render một danh sách các mục dựa trên một mảng. Chỉ thị v-for
yêu cầu một cú pháp đặc biệt theo dạng item in items
, trong đó items
là mảng dữ liệu nguồn và item
là một bí danh cho phần tử của mảng đang được lặp qua:
js
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
template
<li v-for="item in items">
{{ item.message }}
</li>
Trong phạm vi của v-for
, các biểu thức mẫu có quyền truy cập vào tất cả các thuộc tính của phạm vi cha. Ngoài ra, v-for
cũng hỗ trợ một bí danh tùy chọn thứ hai cho chỉ số của mục hiện tại:
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>
Quy tắc phạm vi biến của v-for
tương tự như JavaScript dưới đây:
js
const parentMessage = 'Parent'
const items = [
/* ... */
]
items.forEach((item, index) => {
// có quyền truy cập vào phạm vi ngoại vi `parentMessage`
// nhưng `item` và `index` chỉ có sẵn ở đây
console.log(parentMessage, item.message, index)
})
Lưu ý cách giá trị v-for
khớp với chữ ký hàm của callback forEach
. Thực tế, bạn có thể sử dụng hủy cấu trúc cho bí danh mục v-for
tương tự như hủy cấu trúc đối số hàm:
template
<li v-for="{ message } in items">
{{ message }}
</li>
<!-- với bí danh chỉ số -->
<li v-for="({ message }, index) in items">
{{ message }} {{ index }}
</li>
Đối với v-for
lồng nhau, quy tắc phạm vi cũng hoạt động tương tự như hàm lồng nhau. Mỗi phạm vi v-for
có quyền truy cập vào các phạm vi cha:
template
<li v-for="item in items">
<span v-for="childItem in item.children">
{{ item.message }} {{ childItem }}
</span>
</li>
Bạn cũng có thể sử dụng of
làm phân tách thay vì in
, để nó gần với cú pháp JavaScript cho các trình lặp:
template
<div v-for="item of items"></div>
v-for
với Một Đối Tượng
Bạn cũng có thể sử dụng v-for
để lặp qua các thuộc tính của một đối tượng. Thứ tự lặp sẽ dựa trên kết quả của việc gọi Object.keys()
trên đối tượng:
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>
Bạn cũng có thể cung cấp một bí danh thứ hai cho tên của thuộc tính (còn được gọi là khóa):
template
<li v-for="(value, key) in myObject">
{{ key }}: {{ value }}
</li>
Và một bí danh khác cho chỉ số:
template
<li v-for="(value, key, index) in myObject">
{{ index }}. {{ key }}: {{ value }}
</li>
v-for
với Một Dãy Số
v-for
cũng có thể nhận một số nguyên. Trong trường hợp này, nó sẽ lặp lại mẫu đó nhiều lần, dựa trên một dãy số 1...n
.
template
<span v-for="n in 10">{{ n }}</span>
Lưu ý ở đây n
bắt đầu với giá trị ban đầu là 1
thay vì 0
.
v-for
trên <template>
Tương tự như v-if
của mẫu, bạn cũng có thể sử dụng thẻ <template>
với v-for
để render một khối nhiều phần tử. Ví dụ:
template
<ul>
<template v-for="item in items">
<li>{{ item.msg }}</li>
<li class="divider" role="presentation"></li>
</template>
</ul>
v-for
với v-if
Lưu ý
Không khuyến khích việc sử dụng v-if
và v-for
trên cùng một phần tử do quy định ưu tiên ngầm. Xem chi tiết tại hướng dẫn về kiểu.
Khi chúng tồn tại trên cùng một nút, v-if
có độ ưu tiên cao hơn so với v-for
. Điều này có nghĩa là điều kiện v-if
sẽ không có quyền truy cập vào các biến từ phạm vi của v-for
:
template
<!--
Điều này sẽ gây lỗi vì thuộc tính "todo"
không được định nghĩa trên instance.
-->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
Vấn đề này có thể được sửa bằng cách di chuyển v-for
vào thẻ <template>
bọc nó (điều này cũng làm cho nó rõ ràng hơn):
template
<template v-for="todo in todos">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
Giữ Trạng Thái với key
Khi Vue đang cập nhật một danh sách các phần tử được render bằng v-for
, mặc định nó sử dụng chiến lược "patch" "tại chỗ". Nếu thứ tự của các mục dữ liệu đã thay đổi, thay vì di chuyển các phần tử DOM để phản ánh thứ tự của các mục, Vue sẽ patch từng phần tử tại chỗ và đảm bảo rằng nó phản ánh điều gì nên được render tại chỉ mục cụ thể đó.
Chế độ mặc định này là hiệu quả, nhưng chỉ thích hợp khi đầu ra của bạn không phụ thuộc vào trạng thái của các phần tử con hoặc trạng thái DOM tạm thời (ví dụ: giá trị đầu vào của biểu mẫu không).
Để cung cấp một gợi ý cho Vue để nó có thể theo dõi danh tính của mỗi nút, và do đó, tái sử dụng và sắp xếp các phần tử hiện tại, bạn cần cung cấp một thuộc tính key
duy nhất cho mỗi mục:
template
<div v-for="item in items" :key="item.id">
<!-- nội dung -->
</div>
Khi sử dụng <template v-for>
, key
nên được đặt trên phần tử <template>
:
template
<template v-for="todo in todos" :key="todo.name">
<li>{{ todo.name }}</li>
</template>
Lưu ý
key
ở đây là một thuộc tính đặc biệt được gắn với v-bind
. Đừng nhầm lẫn với biến khóa thuộc tính khi sử dụng v-for
với một đối tượng.
Được khuyến khích cung cấp thuộc tính key
với v-for
mỗi khi có thể, trừ khi đầu ra của danh sách đơn giản (ví dụ: không chứa thành phần hoặc phần tử DOM có trạng thái).
Ràng buộc key
mong đợi các giá trị nguyên tố - tức là chuỗi và số. Không sử dụng đối tượng làm key
cho v-for
. Đối với cách sử dụng chi tiết của thuộc tính key
, xin xem tài liệu API về key
.
v-for
với Một Component
Phần này giả sử bạn có kiến thức về Components. Hãy thoải mái bỏ qua và quay lại sau.
Bạn có thể sử dụng v-for
trực tiếp trên một component, giống như bất kỳ phần tử nào khác (đừng quên cung cấp một key
):
template
<MyComponent v-for="item in items" :key="item.id" />
Tuy nhiên, điều này không tự động chuyển bất kỳ dữ liệu nào vào component, vì component có phạm vi cô lập của riêng chúng. Để chuyển dữ liệu đã được lặp vào component, chúng ta cũng nên sử dụng props:
template
<MyComponent
v-for="(item, index) in items"
:item="item"
:index="index"
:key="item.id"
/>
Lý do không
tự động chèn item
vào component là vì điều này làm cho component liên kết chặt chẽ với cách v-for
hoạt động. Sự rõ ràng về nơi dữ liệu của nó đến giúp component có thể tái sử dụng trong các tình huống khác nhau.
Kiểm tra ví dụ này về danh sách công việc đơn giản để xem cách hiển thị một danh sách các component bằng v-for
, chuyển dữ liệu khác nhau vào mỗi trường hợp.
Phát Hiện Thay Đổi trong Mảng
Các Phương Thức Điều Chỉnh
Vue có khả năng phát hiện khi các phương thức điều chỉnh của một mảng reactive được gọi và kích hoạt các cập nhật cần thiết. Các phương thức điều chỉnh này bao gồm:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
Thay Thế Một Mảng
Các phương thức điều chỉnh, như tên gọi, sửa đổi mảng gốc mà chúng được gọi. So với đó, cũng có các phương thức không sửa đổi, chẳng hạn như filter()
, concat()
và slice()
, không sửa đổi mảng gốc mà luôn trả về một mảng mới. Khi làm việc với các phương thức không sửa đổi, chúng ta nên thay thế mảng cũ bằng mảng mới:
js
// `items` là một ref với giá trị là một mảng
items.value = items.value.filter((item) => item.message.match(/Foo/))
Bạn có thể nghĩ rằng điều này sẽ khiến Vue vứt bỏ DOM hiện tại và render lại toàn bộ danh sách - may mắn thay, điều này không đúng. Vue thực hiện một số heuristics thông minh để tối đa hóa việc tái sử dụng phần tử DOM, vì vậy việc thay thế một mảng bằng một mảng khác chứa các đối tượng trùng lặp là một hoạt động rất hiệu quả.
Hiển Thị Kết Quả Được Lọc/Được Sắp Xếp
Đôi khi, chúng ta muốn hiển thị một phiên bản đã được lọc hoặc sắp xếp của một mảng mà không thực sự sửa đổi hoặc thiết lập lại dữ liệu gốc. Trong trường hợp này, bạn có thể tạo một thuộc tính computed trả về mảng đã được lọc hoặc sắp xếp.
Ví dụ:
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>
Trong những tình huống mà computed property không thích hợp (ví dụ: bên trong các vòng lặp v-for
lồng nhau), bạn có thể sử dụng một phương thức:
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>
Hãy cẩn thận với reverse()
và sort()
trong computed property! Hai phương thức này sẽ sửa đổi mảng gốc, điều này nên được tránh trong computed getters. Hãy tạo một bản sao của mảng gốc trước khi gọi các phương thức này:
diff
- return numbers.reverse()
+ return [...numbers].reverse()