Bài 3: Composition API

1. setup() – điểm khởi đầu của Composition API

Mọi logic của component được khai báo trong setup() hoặc gọn hơn là <script setup>.

<script setup>
import { ref } from 'vue'

const message = ref('Xin chào từ Composition API!')
</script>

<template>
  <h1>{{ message }}</h1>
</template>

Ở đây, ref giúp tạo biến reactive (sẽ giải thích kỹ ở phần sau).

2. ref() – Reactive cho giá trị đơn

ref dùng cho kiểu dữ liệu nguyên thủy (string, number, boolean…).

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++
}
</script>

<template>
  <button @click="increment">+1</button>
  <p>Giá trị: {{ count }}</p>
</template>

📌 Lưu ý: khi dùng trong JS phải .value, nhưng trong template thì không cần.

3. reactive() – Reactive cho Object

Nếu dữ liệu là object hoặc array, dùng reactive().

<script setup>
import { reactive } from 'vue'

const user = reactive({
  name: 'Thanh',
  age: 25
})

function birthday() {
  user.age++
}
</script>

<template>
  <p>Tên: {{ user.name }}</p>
  <p>Tuổi: {{ user.age }}</p>
  <button @click="birthday">Thêm tuổi</button>
</template>

4. computed() – Giá trị tính toán

computed cho phép tính toán dựa trên state, tự động cập nhật khi state thay đổi.

<script setup>
import { ref, computed } from 'vue'

const firstName = ref('Nguyễn')
const lastName = ref('Thành')

const fullName = computed(() => firstName.value + ' ' + lastName.value)
</script>

<template>
  <input v-model="firstName">
  <input v-model="lastName">
  <p>Họ tên đầy đủ: {{ fullName }}</p>
</template>

5. watch() – Theo dõi sự thay đổi

watch dùng để chạy code khi giá trị thay đổi.

<script setup>
import { ref, watch } from 'vue'

const age = ref(20)

watch(age, (newVal, oldVal) => {
  console.log(`Tuổi thay đổi từ ${oldVal} → ${newVal}`)
})
</script>

<template>
  <input type="number" v-model="age">
  <p>Tuổi hiện tại: {{ age }}</p>
</template>

6. provide & inject – Chia sẻ dữ liệu giữa component cha và con sâu

Thay vì truyền props qua nhiều cấp, ta dùng provide (cha) và inject (con).

App.vue (cha)

<script setup>
import { ref, provide } from 'vue'
import Child from './components/Child.vue'

const theme = ref('dark')

// cung cấp cho con
provide('theme', theme)
</script>

<template>
  <h1>App</h1>
  <Child />
</template>

Child.vue (con)

<script setup>
import { inject } from 'vue'

const theme = inject('theme')
</script>

<template>
  <p>Theme từ cha: {{ theme }}</p>
</template>

7. Tạo Composable – logic tái sử dụng

Composable = 1 function bắt đầu bằng use... để gom logic.

useCounter.js

import { ref } from 'vue'

export function useCounter() {
  const count = ref(0)
  const increment = () => count.value++
  const decrement = () => count.value--

  return { count, increment, decrement }
}

App.vue

<script setup>
import { useCounter } from './composables/useCounter'

const { count, increment, decrement } = useCounter()
</script>

<template>
  <button @click="decrement">-</button>
  <span>{{ count }}</span>
  <button @click="increment">+</button>
</template>

📌 Giống như custom hook trong React.

8. script setup – Cách viết gọn

Vue 3 hỗ trợ <script setup> để bỏ qua nhiều boilerplate.

Ví dụ thay vì:

<script>
import { ref } from 'vue'

export default {
  setup() {
    const message = ref('Hello')
    return { message }
  }
}
</script>

Ta viết:

<script setup>
import { ref } from 'vue'
const message = ref('Hello')
</script>

🎯 Kết luận

Trong bài này, bạn đã học:

  • setup()<script setup>
  • Reactive data với ref, reactive
  • computed, watch
  • provide / inject
  • Tạo composable để tái sử dụng logic