Chào mừng độc giả đến với Cafedev! Trong bài viết này, chúng ta sẽ khám phá về List Rendering trong Vue.js. List Rendering là một trong những tính năng quan trọng của Vue.js, cho phép chúng ta render danh sách các phần tử dựa trên dữ liệu từ một mảng. Thông qua List Rendering, chúng ta có thể linh hoạt hiển thị các danh sách, menu, bảng, và nhiều hơn nữa trên giao diện người dùng. Hãy cùng tìm hiểu chi tiết về cách sử dụng và áp dụng List Rendering trong ứng dụng Vue.js của bạn!

1.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 dưới 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ử mảng đang được lặp lại:

const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
<li v-for="item in items">
  {{ item.message }}
</li>

Bên 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 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:

const parentMessage = ref('Parent')
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])
<li v-for="(item, index) in items">
  {{ parentMessage }} - {{ index }} - {{ item.message }}
</li>


Hãy thử nó trong Công cụ Playground
Phạm vi biến của v-for tương tự như trong JavaScript:

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)
})

Chú ý cách giá trị của v-for tương ứng với chữ ký của hàm callback forEach. Trên thực tế, bạn có thể sử dụng destructuring trên bí danh mục v-for tương tự như destructuring đối số hàm:

<li v-for="{ message } in items">
  {{ message }}
</li>

<!-- with index alias -->
<li v-for="({ message }, index) in items">
  {{ message }} {{ index }}
</li>

Đối với v-for lồng nhau, phạm vi cũng hoạt động tương tự như các hàm lồng nhau. Mỗi phạm vi v-for đều có quyền truy cập vào các phạm vi cha:

<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 như là dấu phân cách thay vì in, để nó gần với cú pháp của JavaScript cho iterators:

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

2. v-for với một Object

Bạn cũng có thể sử dụng v-for để lặp qua các thuộc tính của một object. Thứ tự lặp sẽ dựa trên kết quả của việc gọi Object.keys() trên object:

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>

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 gọi là key):

<li v-for="(value, key) in myObject">
  {{ key }}: {{ value }}
</li>

Và một bí danh khác cho chỉ số:

<li v-for="(value, key, index) in myObject">
  {{ index }}. {{ key }}: {{ value }}
</li>

Hãy thử nó trong Công cụ Playground

3.v-for với một Range

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 đó số lần tương ứng, dựa trên một phạm vi từ 1...n.

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

Lưu ý rằng ở đây n bắt đầu với giá trị ban đầu là 1 thay vì 0.

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

4.v-for với v-if

Cảnh báo Lưu Ý: Không khuyến khích sử dụng v-ifv-for trên cùng một phần tử do ưu tiên ngầm định. Tham khảo hướng dẫn về kiểu dáng để biết chi tiết.

Khi chúng tồn tại trên cùng một nút, v-if có ưu tiên cao hơn v-for. Điều đó có nghĩa là điều kiện v-if sẽ không truy cập được vào các biến từ phạm vi của v-for:

<!--
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>
<template v-for="todo in todos">
  <li v-if="!todo.isComplete">
    {{ todo.name }}
  </li>
</template>

5. 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 với v-for, mặc định nó sử dụng một 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ù hợp với 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 hiệu quả, nhưng chỉ phù hợp khi đầu ra của danh sách render không phụ thuộc vào trạng thái của thành phần con hoặc trạng thái DOM tạm thời (ví dụ: giá trị đầu vào của form).

Để cung cấp cho Vue một gợi ý để nó có thể theo dõi danh tính của từng nút, và do đó tái sử dụng và sắp xếp lại các phần tử hiện có, bạn cần cung cấp một thuộc tính key duy nhất cho mỗi mục:

<div v-for="item in items" :key="item.id">
  <!-- content -->
</div>
<template v-for="todo in todos" :key="todo.name">
  <li>{{ todo.name }}</li>

Lưu Ýkey ở đây là một thuộc tính đặc biệt được ràng buộc với v-bind. Nó không nên bị nhầm lẫn với biến khóa thuộc tính khi sử dụng v-for với một object

Được khuyến nghị cung cấp một thuộc tính key với v-for mỗi khi có thể, trừ khi nội dung DOM được lặp lại là đơn giản (tức là không chứa các thành phần hoặc các phần tử DOM có trạng thái), hoặc bạn cố ý phụ thuộc vào hành vi mặc định để có được hiệu suất tốt hơn.
Ràng buộc key mong đợi các giá trị nguyên thủy – tức là chuỗi và số. Đừng sử dụng các đối tượng như là các khóa v-for. Để biết cách sử dụng chi tiết của thuộc tính key, vui lòng xem tài liệu API về key

6.v-for với một Component

Phần này giả định kiến thức về Components. Hãy thoải mái bỏ qua nó và quay lại sau.
Bạn có thể trực tiếp sử dụng v-for 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):

<MyComponent v-for="item in items" :key="item.id" />

Tuy nhiên, điều này sẽ không tự động truyền bất kỳ dữ liệu nào vào component, vì các component có phạm vi cục bộ của riêng chúng. Để truyền dữ liệu được lặp lại vào component, chúng ta cũng nên sử dụng props:

<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 đó làm cho component quá chặt chẽ với cách v-for hoạt động. Việc làm rõ ràng về nguồn dữ liệu của nó làm cho component có thể tái sử dụng trong các tình huống khác.

Xem ví dụ này về một danh sách công việc đơn giản để xem cách render một danh sách các component bằng v-for, truyền dữ liệu khác nhau vào từng trường hợp.

7. Phát hiện Thay Đổi Mảng

7.1 Các Phương Thức Đột Biến

Vue có thể phát hiện khi các phương thức đột biến 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 đột biến này bao gồm:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()

7.2 Thay Thế Một Mảng

Các phương thức đột biến, như tên gọi của chúng, sẽ biến đổ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 đột biến, ví dụ như filter(), concat()slice(), không biến đổ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 đột biến, chúng ta nên thay thế mảng cũ bằng mảng mới:

// `items` is a ref with array value
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 loại bỏ DOM hiện có và render lại toàn bộ danh sách – may mắn thay, điều đó không phải là đú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ả.

8. Hiển Thị Kết Quả Được Lọ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ự biến đổ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 tính toán trả về mảng đã được lọc hoặc sắp xếp.
Ví dụ:

const numbers = ref([1, 2, 3, 4, 5])

const evenNumbers = computed(() => {
  return numbers.value.filter((n) => n % 2 === 0)
})
data() {
  return {
    numbers: [1, 2, 3, 4, 5]
  }
},
computed: {
  evenNumbers() {
    return this.numbers.filter(n => n % 2 === 0)
  }
}
<li v-for="n in evenNumbers">{{ n }}</li>

Trong các tình huống mà thuộc tính tính toán không khả thi (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:

const sets = ref([
  [1, 2, 3, 4, 5],
  [6, 7, 8, 9, 10]
])

function even(numbers) {
  return numbers.filter((number) => number % 2 === 0)
}
<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()sort() trong một thuộc tính tính toán! Hai phương thức này sẽ biến đổi mảng gốc, điều này nên được tránh trong các getter tính toán. 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()

Trên đây là một cái nhìn tổng quan về cách sử dụng List Rendering trong Vue.js. Chúng ta đã khám phá cách sử dụng directive `v-for` để render danh sách từ một mảng dữ liệu, cũng như cách làm việc với các phương thức không đột biến khi cần thiết. Hy vọng rằng bài viết này đã giúp bạn hiểu rõ hơn về cách sử dụng List Rendering để tạo ra các giao diện linh hoạt và hiệu quả trong ứng dụng Vue.js của bạn. Đừng quên theo dõi Cafedev để cập nhật thêm nhiều thông tin hữu ích về Vue.js và các công nghệ khác!

Các nguồn kiến thức MIỄN PHÍ VÔ GIÁ từ cafedev tại đây

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của CafeDev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!