1. ref()——反應式原語
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0) // Ref<number>
const name = ref('Vue') // Ref<string>
const isVisible = ref(true) // Ref<boolean>
const user = ref<User | null>(null) // Ref<User | null>
// Truy cập qua .value trong script
console.log(count.value) // 0
count.value++
// Trong template — auto unwrap (không cần .value)
</script>
<template>
<p>{{ count }}</p>
<p>{{ name }}</p>
</template>
2.reactive()——響應式對象
<script setup lang="ts">
import { reactive } from 'vue'
interface FormState {
name: string
email: string
age: number
}
const form = reactive<FormState>({
name: '',
email: '',
age: 0,
})
// Truy cập trực tiếp (không cần .value)
form.name = 'Duy'
form.age = 25
// ⚠️ Không reassign — sẽ mất reactivity
// form = { name: 'New', email: '', age: 0 } // ❌
</script>
3.compute()——派生狀態
<script setup lang="ts">
import { ref, computed } from 'vue'
const firstName = ref('Duy')
const lastName = ref('Tran')
// Read-only computed
const fullName = computed(() => `${firstName.value} ${lastName.value}`)
// Writable computed
const fullNameWritable = computed({
get: () => `${firstName.value} ${lastName.value}`,
set: (value: string) => {
const [first, ...rest] = value.split(' ')
firstName.value = first
lastName.value = rest.join(' ')
},
})
// Computed với TypeScript
const items = ref<{ price: number; qty: number }[]>([])
const total = computed(() =>
items.value.reduce((sum, item) => sum + item.price * item.qty, 0)
)
</script>
4.watch() 和 watchEffect()
<script setup lang="ts">
import { ref, watch, watchEffect } from 'vue'
const searchQuery = ref('')
const page = ref(1)
// Watch single ref
watch(searchQuery, (newVal, oldVal) => {
console.log(`Search: ${oldVal} → ${newVal}`)
page.value = 1 // Reset page khi search thay đổi
})
// Watch multiple sources
watch([searchQuery, page], ([newQuery, newPage], [oldQuery, oldPage]) => {
fetchResults(newQuery, newPage)
})
// Watch with options
watch(
() => form.name,
(newName) => { validateName(newName) },
{ immediate: true, deep: false }
)
// watchEffect — auto-track dependencies
watchEffect(() => {
// Tự động track searchQuery.value và page.value
console.log(`Fetching: ${searchQuery.value}, page ${page.value}`)
})
// Cleanup
watchEffect((onCleanup) => {
const controller = new AbortController()
fetch(`/api/search?q=${searchQuery.value}`, { signal: controller.signal })
onCleanup(() => controller.abort())
})
</script>
5. 組合 API 與選項 API
<!-- ❌ Options API (legacy) -->
<script lang="ts">
export default {
data() {
return { count: 0 }
},
computed: {
doubled() { return this.count * 2 }
},
methods: {
increment() { this.count++ }
},
}
</script>
<!-- ✅ Composition API (recommended) -->
<script setup lang="ts">
import { ref, computed } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() { count.value++ }
</script>
6. TypeScript 集成
<script setup lang="ts">
// Props với TypeScript
const props = defineProps<{
title: string
count?: number
items: string[]
}>()
// Props với defaults
const props = withDefaults(defineProps<{
title: string
count?: number
}>(), {
count: 0,
})
// Emits với TypeScript
const emit = defineEmits<{
(e: 'update', value: string): void
(e: 'delete', id: number): void
}>()
emit('update', 'new value')
emit('delete', 123)
</script>
下一篇: 組件、道具和活動 — 插槽、提供/注入、生命週期掛鉤。