> For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt.

# Computed Methods and Watch

Ba option này đều khai báo trong options object của component nhưng phục vụ ba mục đích khác nhau:

- `computed`: tạo ra **derived state** (giá trị mới tính từ state khác), có cache.
- `methods`: hàm thực thi theo yêu cầu (event handler, gọi trực tiếp trong template).
- `watch`: chạy **side effect** khi một state thay đổi.

## Computed

Tạo giá trị tính toán dựa trên reactive state (`data`, `props`, hoặc `computed` khác). Vue tự cập nhật khi dependency thay đổi.

### Caching

Computed có cache theo dependency: chỉ tính lại khi một trong các reactive dependency của nó đổi. Truy cập nhiều lần mà dependency không đổi thì trả về giá trị cache, không chạy lại getter.

```js
computed: {
  expensiveCalculation() {
    console.log('computed chạy')
    return this.items.reduce((a, b) => a + b, 0)
  }
}
```

```html
{{ expensiveCalculation }}
{{ expensiveCalculation }}
{{ expensiveCalculation }}
```

Nếu `items` không đổi, getter chỉ chạy 1 lần. Hai lần truy cập sau lấy từ cache. Đây là điểm khác cốt lõi so với `methods` (methods chạy lại mỗi lần render).

### Cú pháp getter-only

Dạng phổ biến nhất, chỉ đọc:

```js
export default {
  data() {
    return {
      firstName: 'Nhan',
      lastName: 'Nguyen'
    }
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
}
```

```html
<p>{{ fullName }}</p>
```

### Cú pháp getter + setter

Computed có thể ghi được khi khai báo dạng object với `get` và `set`. Setter chạy khi gán giá trị cho computed.

```js
computed: {
  fullName: {
    get() {
      return this.firstName + ' ' + this.lastName
    },
    set(value) {
      const parts = value.split(' ')
      this.firstName = parts[0]
      this.lastName = parts[1]
    }
  }
}
```

```js
this.fullName = 'Tran Van'

this.firstName // 'Tran'
this.lastName  // 'Van'
```

Trường hợp dùng setter thường gặp: bind computed vào `v-model` (input cần ghi ngược về state nguồn).

```html
<input v-model="fullName">
```

### Khi nào dùng computed

- Biến đổi state để hiển thị (format, ghép chuỗi).
- Filter / sort danh sách.
- Combine nhiều state thành một giá trị.
- Bất kỳ derived state nào cần dùng lại nhiều nơi.

Ví dụ filter list theo keyword:

```js
data() {
  return {
    keyword: '',
    users: [
      { name: 'An' },
      { name: 'Binh' },
      { name: 'Cuong' }
    ]
  }
},
computed: {
  filteredUsers() {
    return this.users.filter(user =>
      user.name.toLowerCase().includes(this.keyword.toLowerCase())
    )
  }
}
```

```html
<input v-model="keyword">

<ul>
  <li v-for="user in filteredUsers" :key="user.name">
    {{ user.name }}
  </li>
</ul>
```

### Lưu ý

Getter của computed phải là hàm thuần (pure): chỉ tính và trả về giá trị, không gây side effect (không gọi API, không mutate state khác, không thao tác DOM). Cần side effect thì dùng `watch` hoặc `methods`.

## Methods

`methods` là các hàm gắn vào instance. Dùng cho event handler hoặc khi cần gọi logic chủ động.

```js
methods: {
  increment() {
    this.count++
  },
  formatPrice(value) {
    return value.toLocaleString('vi-VN') + ' đ'
  }
}
```

```html
<button @click="increment">+1</button>
<span>{{ formatPrice(product.price) }}</span>
```

Khác biệt quan trọng so với computed: method **không có cache**. Mỗi lần component re-render, mọi method gọi trong template đều chạy lại.

```html
<!-- chạy lại mỗi lần re-render, kể cả khi product.price không đổi -->
{{ formatPrice(product.price) }}

<!-- chạy lại chỉ khi dependency đổi (có cache) -->
{{ formattedPrice }}
```

Quy tắc chọn: giá trị hiển thị dẫn xuất từ state thì ưu tiên `computed`; hành động cần truyền tham số hoặc kích hoạt bởi event thì dùng `methods`.

## Watch

`watch` theo dõi một reactive source và chạy callback (side effect) khi giá trị đổi. Khác `computed` ở chỗ `computed` tạo ra giá trị mới, còn `watch` thực thi hành động.

Các side effect thường gặp:

- Gọi API khi giá trị đổi.
- Debounce search.
- Lưu localStorage.
- Validate form.
- Tracking / log.
- Trigger animation.

### Cú pháp cơ bản

Key của watch là tên property cần theo dõi. Callback nhận `(newValue, oldValue)`.

```js
data() {
  return { keyword: '' }
},
watch: {
  keyword(newVal, oldVal) {
    console.log(`keyword: ${oldVal} -> ${newVal}`)
  }
}
```

### Options: immediate và deep

Dạng object cho phép thêm option:

- `immediate: true`: chạy handler ngay khi component khởi tạo (không cần đợi giá trị đổi). Dùng để load data lần đầu.
- `deep: true`: theo dõi thay đổi sâu bên trong object/array (mặc định watch chỉ phát hiện khi tham chiếu thay đổi).

```js
watch: {
  filters: {
    handler(newVal) {
      this.fetchData(newVal)
    },
    immediate: true, // chạy ngay lần đầu
    deep: true       // theo dõi thay đổi bên trong object filters
  }
}
```

`deep` tốn chi phí vì Vue phải duyệt toàn bộ object. Nếu chỉ cần một field, watch trực tiếp field đó bằng chuỗi path:

```js
watch: {
  'filters.status'(newVal) {
    this.fetchData()
  }
}
```

### Ví dụ debounce search

```js
data() {
  return { keyword: '', results: [] }
},
watch: {
  keyword(newVal) {
    clearTimeout(this._timer)
    this._timer = setTimeout(() => {
      this.search(newVal)
    }, 300)
  }
},
methods: {
  async search(q) {
    const res = await fetch(`/api/search?q=${encodeURIComponent(q)}`)
    this.results = await res.json()
  }
}
```

### vm.$watch

Có thể tạo watcher động bằng `vm.$watch`, trả về hàm để hủy watch:

```js
const unwatch = this.$watch('keyword', (newVal) => {
  console.log(newVal)
})

// hủy theo dõi khi không cần nữa
unwatch()
```

## So sánh Computed vs Methods vs Watch

| Tiêu chí           | computed                 | methods                         | watch                          |
| ------------------ | ------------------------ | ------------------------------- | ------------------------------ |
| Mục đích           | Tạo derived state        | Thực thi logic theo yêu cầu     | Chạy side effect khi state đổi |
| Cache              | Có, theo dependency      | Không                           | Không áp dụng                  |
| Trả về giá trị     | Có (dùng trong template) | Có (gọi thủ công)               | Không (chạy callback)          |
| Gọi trong template | `{{ value }}`            | `{{ fn() }}` hoặc `@event="fn"` | Không trực tiếp                |
| Side effect        | Không nên                | Có thể                          | Mục đích chính                 |
| Nhận tham số       | Không                    | Có                              | Nhận newVal/oldVal             |

Quy tắc nhanh:

- Cần giá trị hiển thị tính từ state: `computed`.
- Cần hành động khi user tương tác hoặc cần truyền tham số: `methods`.
- Cần phản ứng (gọi API, debounce, ghi localStorage) khi state đổi: `watch`.
