Khám Phá Các Tính Năng Cốt Lõi Trong Vue 3: Composition API và Cơ Chế Phản Ứng

Vue 3 giới thiệu một kiến trúc mới dựa trên Composition API, mang lại khả năng tổ chức logic linh hoạt hơn, dễ kiểm soát hơn so với Options API truyền thống. Dưới đây là tổng quan kỹ thuật về các thành phần then chốt — từ cách khai báo dữ liệu phản ứng đến quản lý vòng đời và giao tiếp giữa các thành phần.

1. Hàm setup(): Trung tâm của Composition API

Hàm setup() được gọi trước khi khởi tạo instance, thay thế cho beforeCreatecreated. Nó nhận hai tham số: props (đối tượng thuộc tính đã khai báo) và context (chứa attrs, slots, emit). Hàm này không được khai báo là async nếu muốn trả về dữ liệu trực tiếp cho template — vì Promise sẽ làm mất khả năng truy cập thuộc tính trong template.

Ví dụ minh họa:

<template>
  <div>
    <h2>Xin chào {{ fullName }}</h2>
    <button @click="updateName">Đổi tên</button>
  </div>
</template>

<script>
export default {
  name: 'UserProfile',
  setup(props, { emit }) {
    const firstName = 'Nguyễn';
    const lastName = 'Văn A';

    const updateName = () => {
      // Gửi sự kiện lên cha
      emit('name-updated', `${firstName} ${lastName}`);
    };

    return {
      fullName: `${firstName} ${lastName}`,
      updateName
    };
  }
};
</script>

2. ref()reactive(): Hai cơ chế phản ứng khác nhau

  • ref(value) tạo một đối tượng phản ứng có thuộc tính .value. Dùng chủ yếu cho kiểu nguyên thủy (string, number, boolean), nhưng cũng hỗ trợ đối tượng — lúc đó nó tự động bao bọc bằng reactive().
  • reactive(obj) tạo một Proxy sâu cho đối tượng hoặc mảng. Không cần .value, nhưng không hỗ trợ kiểu nguyên thủy.

Sự khác biệt rõ ràng khi sử dụng:

import { ref, reactive } from 'vue';

const count = ref(0);                    // → count.value = 5
const user = reactive({ name: 'Lan' });  // → user.name = 'Hoa'

// Khi cần trích xuất thuộc tính riêng lẻ từ reactive:
const { name } = toRefs(user); // name là ref, có .value

3. Nguyên lý phản ứng: Từ Object.defineProperty sang Proxy

Vue 2 phụ thuộc vào Object.defineProperty để "bắt" thao tác đọc/ghi thuộc tính — dẫn đến hạn chế như không phát hiện thêm/xóa thuộc tính hoặc thay đổi mảng qua chỉ số. Vue 3 sử dụng Proxy để bao bọc toàn bộ đối tượng, kết hợp Reflect để thực thi các thao tác gốc một cách an toàn và đầy đủ:

const source = { id: 1, tags: ['js'] };
const proxy = new Proxy(source, {
  get(target, key) {
    console.log(`Đang truy cập: ${key}`);
    return Reflect.get(target, key);
  },
  set(target, key, value) {
    console.log(`Cập nhật ${key} → ${value}`);
    return Reflect.set(target, key, value);
  }
});

4. Giám sát và tính toán: watch, computed, watchEffect

  • computed(() => ...) tạo giá trị suy dẫn, tự động tái tính khi phụ thuộc thay đổi.
  • watch(source, callback, options) phản ứng theo thay đổi cụ thể — hỗ trợ giám sát sâu (deep: true) hoặc từng thuộc tính riêng lẻ (dùng hàm getter).
  • watchEffect(callback) tự động thu thập phụ thuộc trong lần chạy đầu tiên — không cần chỉ định nguồn rõ ràng, rất tiện cho side-effect.

Ví dụ giám sát thuộc tính lồng sâu:

const profile = reactive({
  contact: { email: 'a@example.com', phone: '090...' }
});

// Chỉ giám sát email, không kích hoạt deep thừa
watch(() => profile.contact.email, (newVal) => {
  console.log('Email đã thay đổi:', newVal);
});

5. Chu kỳ sống: API dạng hàm và tương thích ngược

Các hook cũ như beforeDestroy được đổi tên thành beforeUnmount. Vue 3 cung cấp các hàm hook tương ứng dưới dạng Composition API: onMounted, onUpdated, onBeforeUnmount, v.v. Chúng được gọi ngay trong setup() và ưu tiên thực thi trước các hook dạng option.

6. Truyền dữ liệu tổ tiên – hậu duệ: provide / inject

Một giải pháp nhẹ để chia sẻ trạng thái giữa các thành phần không trực tiếp liên quan:

// Thành phần cha (hoặc tổ tiên)
setup() {
  const theme = reactive({ color: '#333', fontSize: '16px' });
  provide('appTheme', theme);
  return { theme };
}

// Thành phần con (ở bất kỳ cấp độ nào)
setup() {
  const theme = inject('appTheme');
  return { theme };
}

7. Các hàm tiện ích nâng cao

  • toRef()toRefs(): Tạo ref trỏ tới thuộc tính của đối tượng reactive, giúp giữ tính phản ứng khi phân rã.
  • shallowRef() / shallowReactive(): Chỉ phản ứng ở mức độ nông — hữu ích khi xử lý cấu trúc dữ liệu lớn nhưng thay đổi chủ yếu ở tầng trên.
  • readonly() / shallowReadonly(): Đảm bảo dữ liệu không bị sửa từ bên ngoài — đặc biệt phù hợp khi truyền xuống thành phần con.
  • customRef(): Cho phép kiểm soát chi tiết việc theo dõi và kích hoạt — ví dụ: triển khai debounce cho input tìm kiếm.

8. Các tính năng cấu trúc mới

  • Fragment: Cho phép template không cần thẻ bao ngoài duy nhất — nhiều nút cùng cấp được render trực tiếp.
  • Teleport: Di chuyển DOM ra khỏi vị trí ban đầu (ví dụ: modal vào <body>), tránh vấn đề overflow hoặc z-index.
  • Suspense: Xử lý tải bất đồng bộ với fallback UI — dùng kèm defineAsyncComponent() để cải thiện trải nghiệm người dùng.

Thẻ: Vue3 composition-api Reactive proxy Watch

Đăng vào ngày 10 tháng 7 lúc 16:37