Triển khai đồng bộ dữ liệu hai chiều giữa component cha và con trong Vue 3

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

  1. Component cha truyền đối tượng phản ứng thông qua `v-model`.
  2. Component con nhận và sao chép sâu để tạo bản sao cục bộ.
  3. 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).
  4. 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).
  5. 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

  1. 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.
  2. 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.
  3. 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.

Thẻ: Vue.js Vue 3 Composition API v-model two-way-binding

Đăng vào ngày 10 tháng 6 lúc 17:56