Trong Vue 3, các bước để thực hiện đồng bộ hóa đối tượng phản ứng giữa component cha và con như sau:
Triển khai ý tưởng
- Component cha truyền đối tượng phản ứng thông qua `v-model`.
- Component con nhận và sao chép sâu để tạo bản sao cục bộ.
- Component con theo dõi sự thay đổi dữ liệu từ component cha, cập nhật bản sao cục bộ (không kích hoạt đồng bộ hóa ngược lại).
- Component con theo dõi sự thay đổi của bản sao cục bộ, kích hoạt sự kiện đồng bộ (loại trừ các thay đổi được gây ra bởi component cha).
- Sử dụng cờ `isFromParentUpdate` để phân biệt nguồn thay đổi, ngăn chặn vòng lặp cập nhật.
Triển khai mã
Component cha
<template>
<UserProfile v-model="userData" />
</template>
<script setup>
import { ref } from 'vue'
import UserProfile from './UserProfile.vue'
const userData = ref({
name: 'Người dùng',
profile: { age: 30 }
})
</script>
Component con
<template>
<input v-model="internalState.name">
<input v-model="internalState.profile.age" type="number">
</template>
<script setup>
import { ref, watch, nextTick } from 'vue'
const propsData = defineProps({
externalData: {
type: Object,
required: true
}
})
const emit = defineEmits(['update:externalData'])
// Sao chép sâu để tạo bản sao cục bộ
const internalState = ref(JSON.parse(JSON.stringify(propsData.externalData)))
// Cờ: có phải cập nhật từ component cha không
let isFromParentUpdate = false
// Theo dõi dữ liệu từ component cha
watch(
() => propsData.externalData,
async (newVal) => {
isFromParentUpdate = true
internalState.value = JSON.parse(JSON.stringify(newVal)) // Cập nhật bản sao
await nextTick()
isFromParentUpdate = false
},
{ deep: true }
)
// Theo dõi bản sao cục bộ
watch(
internalState,
(newVal) => {
if (!isFromParentUpdate) {
emit('update:externalData', JSON.parse(JSON.stringify(newVal)))
}
},
{ deep: true }
)
</script>
Điểm chính
- Xử lý sao chép sâu: Sử dụng `JSON.parse(JSON.stringify())` để đảm bảo bản sao không có mối quan hệ tham chiếu với dữ liệu gốc.
- Cơ chế theo dõi hai chiều:
- Cha → Con: Theo dõi `propsData.externalData`, đánh dấu nguồn khi cập nhật bản sao để tránh kích hoạt đồng bộ.
- Con → Cha: Theo dõi `internalState`, kích hoạt sự kiện đồng bộ khi không phải là cập nhật từ component cha.
- Xử lý ngăn chặn vòng lặp: Sử dụng `isFromParentUpdate` và `nextTick()` để đảm bảo cập nhật từ component cha không gây ra đồng bộ ngược lại.
Lưu ý
- Tối ưu hóa hiệu suất: Sao chép sâu không phù hợp với các đối tượng lớn, có thể thay thế bằng `structuredClone` hoặc các thư viện công cụ (như `_.cloneDeep` của lodash).
- Kiểu dữ liệu phản ứng: Nếu component cha sử dụng `reactive()`, cần đảm bảo `propsData.externalData` là một tham chiếu phản ứng.
- Ổn định cấu trúc: Khi cấu trúc đối tượng thay đổi (thêm/xóa thuộc tính), cần sao chép sâu lại để tránh sự không nhất quán dữ liệu.