Appearance
Các Thuộc Tính Tính Toán
Ví Dụ Cơ Bản
Các biểu thức trong mẫu rất thuận tiện, nhưng chúng được thiết kế cho các hoạt động đơn giản. Đặt quá nhiều logic trong mẫu có thể làm cho chúng trở nên rối bời và khó bảo trì. Ví dụ, nếu chúng ta có một đối tượng với một mảng lồng:
js
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
Và chúng ta muốn hiển thị các thông báo khác nhau tùy thuộc vào việc author
đã có sách hay chưa:
template
<p>Đã xuất bản sách:</p>
<span>{{ author.books.length > 0 ? 'Có' : 'Không' }}</span>
Ở điểm này, mẫu trở nên hơi lộn xộn. Chúng ta phải nhìn vào nó một lúc trước khi nhận ra rằng nó thực hiện một phép tính tùy thuộc vào author.books
. Quan trọng hơn, chúng ta có thể không muốn lặp lại nếu chúng ta cần bao gồm phép tính này trong mẫu nhiều hơn một lần.
Chính vì vậy, đối với logic phức tạp bao gồm dữ liệu phản ứng, nên sử dụng một thuộc tính tính toán. Dưới đây là ví dụ tương tự, được tái cấu trúc:
vue
<script setup>
import { reactive, computed } from 'vue'
const author = reactive({
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
})
// một ref tính toán
const messageSachDaXuatBan = computed(() => {
return author.books.length > 0 ? 'Có' : 'Không'
})
</script>
<template>
<p>Đã xuất bản sách:</p>
<span>{{ messageSachDaXuatBan }}</span>
</template>
Ở đây, chúng ta đã khai báo một thuộc tính tính toán messageSachDaXuatBan
. Hàm computed()
mong đợi được truyền một hàm getter, và giá trị trả về là một ref tính toán. Tương tự như ref bình thường, bạn có thể truy cập kết quả tính toán như là messageSachDaXuatBan.value
. Refs tính toán cũng tự động mở gói trong mẫu để bạn có thể tham chiếu đến chúng mà không cần .value
trong biểu thức mẫu.
Một thuộc tính tính toán tự động theo dõi các phụ thuộc phản ứng của nó. Vue nhận biết rằng việc tính toán messageSachDaXuatBan
phụ thuộc vào author.books
, nên nó sẽ cập nhật mọi kết nối phụ thuộc vào messageSachDaXuatBan
khi author.books
thay đổi.
Xem thêm: Gõ Tính Toán
Bộ Nhớ Đệm Của Thuộc Tính Tính Toán So Với Phương Thức
Bạn có thể đã nhận thức được rằng chúng ta có thể đạt được kết quả tương tự bằng cách gọi một phương thức trong biểu thức:
template
<p>{{ calculateBooksMessage() }}</p>
js
// trong component
function calculateBooksMessage() {
return author.books.length > 0 ? 'Có' : 'Không'
}
Thay vì sử dụng một thuộc tính tính toán, chúng ta có thể định nghĩa cùng một hàm dưới dạng một phương thức. Đối với kết quả cuối cùng, hai phương pháp này đều hoàn toàn giống nhau. Tuy nhiên, sự khác biệt là các thuộc tính tính toán được lưu vào bộ nhớ đệm dựa trên các phụ thuộc phản ứng của chúng. Một thuộc tính tính toán chỉ sẽ đánh giá lại khi một số phụ thuộc phản ứng của nó đã thay đổi. Điều này có nghĩa là miễn là author.books
không thay đổi, nhiều lần truy cập đến publishedBooksMessage
sẽ ngay lập tức trả lại kết quả tính toán trước đó mà không cần chạy lại hàm getter.
Điều này cũng có nghĩa là thuộc tính tính toán sau sẽ không bao giờ cập nhật, vì Date.now()
không phải là một phụ thuộc phản ứng:
js
const now = computed(() => Date.now())
So với đó, việc gọi một phương thức sẽ luôn luôn chạy hàm mỗi khi một lần render xảy ra.
Tại sao chúng ta cần sử dụng bộ nhớ đệm? Hãy tưởng tượng rằng chúng ta có một thuộc tính tính toán đắt tiền list
, yêu cầu lặp qua một mảng lớn và thực hiện nhiều phép tính. Sau đó, có thể có các thuộc tính tính toán khác phụ thuộc vào list
. Nếu không có bộ nhớ đệm, chúng ta sẽ thực thi getter của list
nhiều lần hơn là cần thiết! Trong những trường hợp nơi bạn không muốn sử dụng bộ nhớ đệm, hãy sử dụng gọi một phương thức thay vào đó.
Thuộc Tính Tính Toán Có Thể Ghi
Thuộc tính tính toán mặc định chỉ có thể đọc. Nếu bạn cố gắng gán một giá trị mới cho một thuộc tính tính toán, bạn sẽ nhận được một cảnh báo tại thời điểm chạy. Trong những trường hợp hiếm hoi khi bạn cần một thuộc tính tính toán "có thể ghi", bạn có thể tạo một bằng cách cung cấp cả hàm getter và hàm setter:
vue
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
// getter
get() {
return firstName.value + ' ' + lastName.value
},
// setter
set(newValue) {
// Lưu ý: chúng ta sử dụng cú pháp gán hủy ở đây.
;[firstName.value, lastName.value] = newValue.split(' ')
}
})
</script>
Bây giờ khi bạn chạy fullName.value = 'John Doe'
, hàm setter sẽ được gọi và firstName
và lastName
sẽ được cập nhật tương ứng.
Thực Hành Tốt Nhất
Getter Nên Là Không Tác Động Phụ
Quan trọng nhớ rằng các hàm getter của thuộc tính tính toán chỉ nên thực hiện tính toán tinh khiết và không tác động phụ. Ví dụ, **đừng thay đổi trạng thái khác, thực hiện yêu cầu không đồng bộ, hoặc thay đổi DOM bên trong một hà
m getter của thuộc tính tính toán!** Hãy coi thuộc tính tính toán như là mô tả một cách tuyên bố làm thế nào để suy ra một giá trị dựa trên các giá trị khác - trách nhiệm của nó chỉ nên là tính toán và trả về giá trị đó. Sau này trong hướng dẫn, chúng ta sẽ thảo luận về cách chúng ta có thể thực hiện các tác động phụ khi có thay đổi trạng thái với watchers.
Tránh Việc Thay Đổi Giá Trị Tính Toán
Giá trị trả về từ một thuộc tính tính toán là trạng thái suy ra. Hãy coi nó như là một bức ảnh tạm thời - mỗi khi trạng thái nguồn thay đổi, một bức ảnh mới được tạo ra. Không có ý nghĩa gì khi thay đổi một bức ảnh, nên một giá trị trả về từ tính toán nên được coi là chỉ có thể đọc và không bao giờ được thay đổi - thay vào đó, hãy cập nhật trạng thái nguồn nó phụ thuộc vào để kích thích tính toán mới.