Cơ Chế Giám Sát Phản Ứng Với WatchEffect
Vue 3 cung cấp công cụ mạnh mẽ để theo dõi sự thay đổi của trạng thái thông qua watchEffect. Hàm này tự động thu thập các biến được tham chiếu bên trong hiệu ứng của nó mà không cần chỉ định thủ công.
Cú pháp cơ bản như sau:
watchEffect(effect: (onInvalidate: InvalidateCbRegistrator) => void | (() => void) | Promise<void>, options?: WatchEffectOptions): WatchStopHandle
Chi Tiết Tham Số
- effect: Hàm chứa logic nghiệp vụ. Dữ liệu phản ứng (reactive) truy cập trong phạm vi hàm này sẽ được tự động theo dõi. Đối số
onInvalidatecho phép đăng ký hàm dọn dẹp khi phụ thuộc bị thay đổi. Hàm có thể trả về một hàm dọn dẹp hoặc Promise để xử lý bất đồng bộ. - options: Cấu hình tùy chọn như độ sâu (
deep) hoặc thời điểm chạy (flush). - Trả về: Một hàm đóng vai trò như tay cầm để hủy bỏ quan sát.
Tùy Chọn WatchEffectOptions
lazy(Boolean): Nếu làtrue, hiệu ứng sẽ không chạy ngay lập tức mà chờ đến lần đầu tiên phụ thuộc thay đổi. Mặc định làfalse.deep(Boolean): Bật chế độ theo dõi cấu trúc lồng nhau của Object hoặc Array. Mặc định làfalse.flush(String): Xác định thời điểm callback thực thi so với vòng lặp sự kiện. Giá trị mặc định'pre'thực thi trước render nhưng sau DOM update.'post'thực thi sau tất cả sự thay đổi cùng vòng lặp.
Ví dụ Cơ Bản
Giả lập trường hợp hiển thị trạng thái nhập liệu:
<template>
<div>
<p>Giá trị hiện tại: {{ inputData }}</p>
<input v-model="inputData" />
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue';
const inputData = ref('');
watchEffect(() => {
console.log(`Trường dữ liệu đã cập nhật thành: ${inputData.value}`);
});
</script>
Trong ví dụ trên, mỗi khi người dùng gõ chữ vào ô nhập, log sẽ tự động cập nhật giá trị mới do cơ chế tự động theo dõi của framework.
Hủy Bỏ Giám Sát
Kết quả trả về từ watchEffect cho phép tắt listener khi cần thiết:
<template>
<div>
<p>Trạng thái: {{ statusMsg }}</p>
<button @click="haltTracking">Dừng Theo Dõi</button>
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue';
const statusMsg = ref('Đang chạy');
let cancelWatch = null;
const haltTracking = () => {
if (cancelWatch) cancelWatch();
};
// Lưu lại hàm hủy vào biến cục bộ
cancelWatch = watchEffect(() => {
console.log(`Trạng thái hiện hành: ${statusMsg.value}`);
});
</script>
Sử Dụng Watch Để Theo Dõi Cụ Thể
Một lựa chọn khác là watch, phù hợp khi bạn cần xác định rõ nguồn dữ liệu cần kiểm soát và so sánh giá trị cũ/mới.
Tham Số Cốt Lõi
- Source: Có thể nhận chuỗi tên thuộc tính, Ref object hoặc hàm trả về giá trị mong muốn.
- Callback: Chứa xử lý khi dữ liệu thay đổi. Nhận hai đối số: giá trị mới và giá trị cũ.
- Options: Cấu hình thêm như
immediate,deep,flush.
WatchOptions Chi Tiết
immediate: Khởi động callback ngay lúc khai báo nếutrue.deep: Bật theo dõi đệ quy cho các thuộc tính lồng nhau.flush: Quản lý thứ tự thực thi trong vòng lặp sự kiện ('pre'hay'post').onTrack/onTrigger: Cung cấp hook để phân tích hiệu năng hoặc gỡ lỗi quá trình Dependency Tracking.
Lấy Ví Dụ Thực Tế
Cấu hình callback chạy ngay khi component mount:
import { watch } from 'vue';
watch(
() => currentBalance,
(newVal, oldVal) => {
console.log(`Số dư thay đổi từ ${oldVal} sang ${newVal}`);
},
{ immediate: true }
);
Phân Biệt WatchEffect Và Watch
Mặc dù cả hai đều phục vụ mục đích lắng nghe biến đổi, cách tiếp cận khác biệt rõ rệt:
- WatchEffect: Tự động phát hiện dependency. Thích hợp cho tác vụ đơn giản, ít phức tạp trong logic khởi tạo.
- Watch: Yêu cầu định nghĩa rõ ràng source data. Cung cấp khả năng so sánh oldValue/newValue và nhiều tùy chọn nâng cao hơn. Tốt cho các luồng xử lý cần độ chính xác cao.
Minh Họa Sự Khác Biệt
<template>
<div>
<p>Biến A: {{ varA }}</p>
<p>Biến B: {{ varB }}</p>
<button @click="updateBoth">Cập Nhật Cả Hai</button>
</div>
</template>
<script setup>
import { ref, watchEffect, watch } from 'vue';
const varA = ref(0);
const varB = ref(0);
// Tự động theo dõi biến A
watchEffect(() => {
console.log(`Biến A mới là: ${varA.value}`);
});
// Chỉ định rõ ràng biến B cần nghe ngóng
watch(varB, (nv, ov) => {
console.log(`Biến B chuyển từ ${ov} thành ${nv}`);
}, { immediate: true });
function updateBoth() {
varA.value++;
varB.value++;
}
</script>
Khi nhấn nút, cả hai log sẽ xuất hiện do cơ chế trigger tương ứng với từng loại API.
Xử Lý Mảng Và Đối Tượng Sâu
Việc lắng nghe thay đổi nội bộ của mảng yêu cầu cấu hình đặc biệt để đảm bảo mọi item được giám sát.
import { watch } from 'vue';
watch(
() => inventoryList,
(newItems, oldItems) => {
console.log('Danh sách hàng hóa đã thay đổi', newItems);
},
{ deep: true }
);
Khi deep bật, Vue sẽ scan toàn bộ chuỗi property. Lưu ý rằng các phần tử trong mảng phải là đối tượng phản ứng (ref/reactive) thì sự thay đổi của thuộc tính con mới được phát hiện đúng cách.