Vue 3 Reactivity Transform trong 2026: $ref, $computed và Câu hỏi Phỏng vấn
Hướng dẫn chuyên sâu về Reactivity Transform trong Vue 3 bao gồm $ref, $computed, và các câu hỏi phỏng vấn thường gặp cho lập trình viên Vue cấp cao.

Reactivity Transform là tính năng thử nghiệm trong Vue 3 nhằm đơn giản hóa việc sử dụng reactive state bằng cách loại bỏ nhu cầu truy cập .value trên ref. Mặc dù tính năng này đã bị loại bỏ khỏi Vue core từ phiên bản 3.4, việc hiểu về khái niệm và các giải pháp thay thế vẫn có giá trị trong các cuộc phỏng vấn Vue developer vì thể hiện kiến thức sâu về hệ thống reactivity của Vue.
Reactivity Transform ($ref, $computed) đã bị loại bỏ từ Vue 3.4 trở lên. Vue Macros cung cấp implementation từ cộng đồng cho các team vẫn cần tính năng này. Hiểu về khái niệm này vẫn có giá trị cho phỏng vấn vì thể hiện kiến thức về sự phát triển của Vue.
Hiểu vấn đề mà Reactivity Transform giải quyết
Hệ thống reactivity của Vue 3 dựa trên các đối tượng ref và reactive. Khi sử dụng ref, developer phải truy cập giá trị thông qua thuộc tính .value trong script, trong khi ở template việc truy cập trực tiếp không cần .value được cho phép nhờ unwrapping tự động.
import { ref, computed } from 'vue'
// Cách tiếp cận chuẩn với .value
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
console.log(doubled.value)
}Reactivity Transform loại bỏ boilerplate .value bằng các macro compiler biến đổi code tại thời điểm build.
// Với Reactivity Transform (deprecated)
let count = $ref(0)
const doubled = $computed(() => count * 2)
function increment() {
count++
console.log(doubled)
}Cách hoạt động của $ref và $computed
Macro $ref khai báo biến reactive có thể được sử dụng như biến JavaScript thông thường. Compiler của Vue biến đổi code này thành việc sử dụng ref chuẩn với .value phù hợp.
// Source code với $ref
let username = $ref('')
let isValid = $computed(() => username.length >= 3)
function updateUsername(value: string) {
username = value
}
// Kết quả sau khi compiler transform
import { ref, computed } from 'vue'
const username = ref('')
const isValid = computed(() => username.value.length >= 3)
function updateUsername(value: string) {
username.value = value
}Macro $$ để Escape Ref
Macro $$ cho phép truy cập đối tượng ref bên dưới khi cần thiết, ví dụ khi truyền ref vào composable hoặc watch.
import { watch } from 'vue'
let count = $ref(0)
// Sử dụng $$ để lấy ref object
watch($$(count), (newVal, oldVal) => {
console.log(`Count thay đổi từ ${oldVal} sang ${newVal}`)
})
// Truyền ref vào composable
function useDoubled(countRef: Ref<number>) {
return computed(() => countRef.value * 2)
}
const doubled = useDoubled($$(count))Reactive Props Destructure
Vue 3.5 giới thiệu destructuring props reactive như một tính năng ổn định, thay thế một phần chức năng của Reactivity Transform cho props.
<script setup lang="ts">
// Vue 3.5+ reactive props destructure
const { count = 0, title } = defineProps<{
count?: number
title: string
}>()
// count và title vẫn reactive
const doubled = computed(() => count * 2)
</script>
<template>
<h1>{{ title }}</h1>
<p>Count: {{ count }}, Doubled: {{ doubled }}</p>
</template>Trước Vue 3.5, destructuring props làm mất reactivity và cần toRefs:
const props = defineProps<{ count: number }>()
const { count } = toRefs(props) // Bắt buộc để giữ reactivityImplementation thay thế với Vue Macros
Vue Macros là bộ sưu tập macro được duy trì bởi cộng đồng, cung cấp chức năng Reactivity Transform và các tính năng bổ sung khác.
import Vue from '@vitejs/plugin-vue'
import VueMacros from 'unplugin-vue-macros/vite'
export default defineConfig({
plugins: [
VueMacros({
plugins: {
vue: Vue(),
},
}),
],
})<script setup lang="ts">
// Với Vue Macros
let count = $ref(0)
let items = $ref<string[]>([])
const total = $computed(() => items.length)
const isEmpty = $computed(() => total === 0)
function addItem(item: string) {
items = [...items, item]
count++
}
</script>Pattern hiện đại không cần Reactivity Transform
Cách tiếp cận được khuyến nghị trong Vue 3.5 sử dụng các tính năng built-in mà không cần macro bên ngoài.
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
// Quy ước đặt tên: sử dụng suffix Ref để rõ ràng
const countRef = ref(0)
const itemsRef = ref<string[]>([])
// Computed properties
const total = computed(() => itemsRef.value.length)
const isEmpty = computed(() => total.value === 0)
// Composable với reactive state
function useCounter(initialValue = 0) {
const count = ref(initialValue)
const increment = () => count.value++
const decrement = () => count.value--
const reset = () => count.value = initialValue
return {
count,
increment,
decrement,
reset
}
}
const { count, increment, decrement } = useCounter(10)
</script>So sánh ref vs reactive
Việc hiểu khi nào sử dụng ref so với reactive thường xuất hiện trong phỏng vấn Vue.
import { ref, reactive, toRefs } from 'vue'
// ref: cho primitive values và single values
const count = ref(0)
const name = ref('Vue')
const isLoading = ref(false)
// reactive: cho objects với nhiều properties
const state = reactive({
user: null as User | null,
posts: [] as Post[],
pagination: {
page: 1,
perPage: 10
}
})
// Destructure reactive object với toRefs
const { user, posts } = toRefs(state)
// Ưu điểm của ref:
// 1. Có thể reassign hoàn toàn
// 2. Có thể truyền vào functions mà không mất reactivity
// 3. Predictable hơn với TypeScript
// Ưu điểm của reactive:
// 1. Không cần .value cho nested access
// 2. Tự nhiên hơn cho complex state objects
// 3. Automatic unwrapping trong templateAdvanced Reactivity Patterns
Các pattern nâng cao thường được hỏi trong phỏng vấn Vue developer cấp cao.
Custom Ref với Validation
import { customRef } from 'vue'
function useDebouncedRef<T>(value: T, delay = 300) {
let timeout: ReturnType<typeof setTimeout>
return customRef<T>((track, trigger) => ({
get() {
track()
return value
},
set(newValue: T) {
clearTimeout(timeout)
timeout = setTimeout(() => {
value = newValue
trigger()
}, delay)
}
}))
}
// Sử dụng
const searchQuery = useDebouncedRef('', 500)Shallow Reactivity cho Performance
import { shallowRef, shallowReactive, triggerRef } from 'vue'
// shallowRef: chỉ level đầu tiên là reactive
const largeList = shallowRef<Item[]>([])
function updateList(items: Item[]) {
largeList.value = items // Triggers update
}
function mutateItem(index: number) {
largeList.value[index].name = 'Updated'
triggerRef(largeList) // Cần trigger thủ công
}
// shallowReactive: chỉ properties level đầu tiên là reactive
const config = shallowReactive({
theme: 'dark',
settings: { /* nested không reactive */ }
})Sẵn sàng chinh phục phỏng vấn Vue.js / Nuxt.js?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Câu hỏi Phỏng vấn Vue Reactivity
Câu hỏi 1: Giải thích sự khác biệt giữa ref và reactive
Câu trả lời mong đợi: ref bọc bất kỳ giá trị nào (primitive hoặc object) trong một đối tượng có thuộc tính .value, trong khi reactive tạo proxy reactive từ object. ref linh hoạt hơn vì có thể chứa primitive và có thể reassign hoàn toàn. reactive ergonomic hơn cho complex nested objects nhưng không thể reassign và mất reactivity nếu destructure mà không có toRefs.
Câu hỏi 2: Tại sao Reactivity Transform bị loại bỏ khỏi Vue core?
Câu trả lời mong đợi: Team Vue quyết định loại bỏ Reactivity Transform vì một số lý do: thêm mental complexity bằng cách trộn hai model reactivity, yêu cầu tooling đặc biệt không phải lúc nào cũng có sẵn, và làm debugging khó hơn vì code được viết khác với code được chạy. Tính năng reactive props destructure trong Vue 3.5 giải quyết use case phổ biến nhất mà không cần macro toàn cục.
Câu hỏi 3: Làm thế nào để giữ reactivity khi destructuring?
// Câu hỏi: Sửa code này để nó reactive
const state = reactive({ count: 0, name: 'Vue' })
const { count, name } = state // Không reactive!
// Câu trả lời:
import { toRefs } from 'vue'
const state = reactive({ count: 0, name: 'Vue' })
const { count, name } = toRefs(state) // Bây giờ reactive
// Hoặc sử dụng computed cho derived values
const count = computed(() => state.count)Câu hỏi 4: Khi nào sử dụng shallowRef vs ref?
Câu trả lời mong đợi: Sử dụng shallowRef khi làm việc với large objects hoặc arrays thường được replace toàn bộ nhưng hiếm khi bị mutate bên trong. Điều này tránh overhead từ deep reactivity tracking. Ví dụ use case: large API response, large datasets cho bảng, hoặc state được quản lý bởi external library.
Câu hỏi 5: Implement computed writable
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
const fullName = computed({
get() {
return `${firstName.value} ${lastName.value}`
},
set(value: string) {
const parts = value.split(' ')
firstName.value = parts[0] || ''
lastName.value = parts.slice(1).join(' ') || ''
}
})
// Sử dụng
fullName.value = 'Jane Smith' // firstName = 'Jane', lastName = 'Smith'Best Practices Reactivity Vue 3
-
Ưu tiên ref cho composables: Trả về
reftừ composables vì dễ destructure hơn và vẫn giữ reactive. -
Sử dụng readonly cho data không nên bị mutate:
readonly(state)ngăn chặn mutation không mong muốn. -
Tránh deep reactivity không cần thiết: Sử dụng
shallowRefhoặcmarkRawcho data không cần reactive. -
Nhất quán với quy ước đặt tên: Thêm suffix như
RefhoặcStateđể rõ ràng. -
Tận dụng TypeScript: Định nghĩa types cho ref generics để ngăn runtime errors.
Kết luận
Mặc dù Reactivity Transform đã bị loại bỏ khỏi Vue core, việc hiểu các khái niệm đằng sau nó cung cấp insight có giá trị về hệ thống reactivity của Vue. Các tính năng hiện đại như reactive props destructure trong Vue 3.5 cung cấp ergonomics tốt hơn mà không có sự phức tạp thêm từ macro compiler. Trong phỏng vấn, khả năng giải thích các trade-off của các cách tiếp cận reactivity khác nhau thể hiện sự hiểu biết sâu sắc về Vue internals.
Bạn có tìm ra lỗi trong Vue.js / Nuxt.js không?
Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Viết bởi
Anthony Fillion-MailletNgười sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 9 tháng 9, 2026
Thẻ
Chia sẻ
Bài viết liên quan

Vue 3 Composition API: Hướng Dẫn Đầy Đủ Để Làm Chủ Reactivity
Làm chủ Vue 3 Composition API qua hướng dẫn thực hành này. Tìm hiểu ref, reactive, computed, watch và composables để xây dựng ứng dụng Vue hiệu suất cao.

Vue 3 Composables Nâng Cao: Các Pattern Tái Sử Dụng và Câu Hỏi Phỏng Vấn 2026
Phân tích chuyên sâu các composable nâng cao trong Vue 3 với pattern tái sử dụng, xử lý bất đồng bộ, dependency injection qua provide/inject, form validation và testing. Bao gồm câu hỏi phỏng vấn kỹ thuật cập nhật 2026.

Vue 3 Script Setup và defineModel năm 2026: Cú pháp Hiện đại và Câu hỏi Phỏng vấn
Hướng dẫn chi tiết về cú pháp script setup Vue 3, defineModel cho two-way binding, reactive props destructure trong Vue 3.5, và các câu hỏi phỏng vấn thường gặp.