> 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.

# Vue Instance

## Tạo instance

```js
var vm = new Vue({
  // options object
})
```

Quy ước đặt tên `vm` = ViewModel. Mọi component cũng là một Vue instance nên nhận cùng options object, trừ các option chỉ dành cho root instance như `el`.

### Cây component

```
Root Instance (new Vue)
└─ TodoList
   ├─ TodoItem
   │  ├─ TodoButtonDelete
   │  └─ TodoButtonEdit
   └─ TodoListFooter
      ├─ TodosButtonClear
      └─ TodoListStatistics
```

***

## Data & Reactivity

Mọi property trong `data` được đưa vào reactivity system lúc instance khởi tạo. Thay đổi giá trị thì view tự re-render.

```js
var data = { a: 1 }
var vm = new Vue({ data: data })

vm.a === data.a // true — instance proxy tới data gốc

vm.a = 2
data.a // 2

data.a = 3
vm.a // 3
```

### Chỉ reactive nếu khai báo từ đầu

Property chỉ reactive nếu đã tồn tại lúc tạo instance. Thêm property sau đó sẽ không được track:

```js
var vm = new Vue({ data: { a: 1 } })

vm.b = 'hi' // KHÔNG reactive
```

Luôn khai báo trước mọi property với giá trị khởi tạo (kể cả rỗng):

```js
data: {
  newTodoText: '',
  visitCount: 0,
  hideCompletedTodos: false,
  todos: [],
  error: null
}
```

Cần thêm reactive property sau khi tạo thì dùng `vm.$set(obj, key, value)`.

### Object.freeze()

`Object.freeze()` chặn Vue setup getter/setter, làm object không reactive:

```js
var obj = { foo: 'bar' }
Object.freeze(obj)

new Vue({ el: '#app', data: obj })
```

```html
<div id="app">
  <p>{{ foo }}</p>
  <!-- foo không update vì obj đã bị freeze -->
  <button v-on:click="foo = 'baz'">Thay đổi</button>
</div>
```

Dùng cho data read-only lớn (lookup table, config tĩnh) để bỏ overhead reactivity và tiết kiệm bộ nhớ.

***

## Instance Properties & Methods

Tất cả được prefix `$` để tách khỏi property do user định nghĩa.

```js
var data = { a: 1 }
var vm = new Vue({ el: '#example', data: data })

vm.$data === data                              // true
vm.$el === document.getElementById('example')  // true

vm.$watch('a', function (newVal, oldVal) {
  console.log(`a: ${oldVal} -> ${newVal}`)
})
```

### Các instance properties quan trọng

| Property       | Mô tả                                                               |
| -------------- | ------------------------------------------------------------------- |
| `vm.$data`     | Object data của instance                                            |
| `vm.$el`       | DOM element mà instance mount vào                                   |
| `vm.$options`  | Options object được dùng khi khởi tạo                               |
| `vm.$parent`   | Instance cha (nếu có)                                               |
| `vm.$root`     | Root instance của cây component                                     |
| `vm.$children` | Array các component con trực tiếp                                   |
| `vm.$refs`     | Object chứa các DOM elements / child components được đánh dấu `ref` |
| `vm.$slots`    | Object chứa nội dung slot                                           |
| `vm.$attrs`    | Attributes không được khai báo là props                             |

### Các instance methods quan trọng

| Method                     | Mô tả                               |
| -------------------------- | ----------------------------------- |
| `vm.$watch(expr, cb)`      | Theo dõi sự thay đổi của expression |
| `vm.$set(obj, key, val)`   | Thêm reactive property vào object   |
| `vm.$delete(obj, key)`     | Xóa reactive property               |
| `vm.$emit(event, ...args)` | Emit một custom event               |
| `vm.$on(event, cb)`        | Lắng nghe một custom event          |
| `vm.$off(event, cb)`       | Hủy lắng nghe event                 |
| `vm.$nextTick(cb)`         | Callback sau khi DOM update xong    |
| `vm.$mount(el)`            | Mount instance vào DOM element      |
| `vm.$destroy()`            | Destroy instance                    |
| `vm.$forceUpdate()`        | Force re-render (dùng cẩn thận!)    |

***

## Lifecycle Hooks

Mỗi instance đi qua các bước: setup data observation, compile template, mount vào DOM, update DOM khi data thay đổi, teardown khi destroy. Hooks cho phép chèn code tại từng thời điểm.

### Toàn bộ lifecycle hooks

```js
new Vue({
  // --- Khởi tạo ---
  beforeCreate() {
    // Instance vừa tạo, data/events chưa setup; this.$data chưa có
  },

  created() {
    // Data, computed, watch, methods đã sẵn sàng; DOM chưa mount ($el chưa có)
    // Dùng cho: gọi API, khởi tạo state
    console.log('created - a is:', this.a)
  },

  // --- Mount vào DOM ---
  beforeMount() {
    // Template đã compile, sắp mount; hầu như không dùng
  },

  mounted() {
    // Instance đã mount ($el tồn tại)
    // Dùng cho: thao tác DOM, init thư viện third-party
    console.log('mounted - el:', this.$el)
  },

  // --- Cập nhật ---
  beforeUpdate() {
    // Data đã đổi, DOM sắp re-render; có thể đọc DOM cũ trước khi patch
  },

  updated() {
    // DOM đã re-render; tránh thay đổi data ở đây để khỏi loop vô hạn
  },

  // --- Destroy ---
  beforeDestroy() {
    // Instance sắp destroy; dùng cho: cleanup listeners, timers, subscriptions
  },

  destroyed() {
    // Instance đã destroy; mọi directive và event listener đã unbind
  }
})
```

### Sơ đồ Lifecycle

```mermaid
graph TD
  A["new Vue(options)"] --> B[beforeCreate]
  B --> C["Setup: data, events, watch"]
  C --> D[created]
  D --> E{Có el option?}
  E -- Không --> F["Chờ vm.$mount(el)"]
  F --> G{Có template option?}
  E -- Có --> G
  G -- Có template --> H["Compile template → render fn"]
  G -- Không có template --> I["Compile outerHTML của el → render fn"]
  H --> J[beforeMount]
  I --> J
  J --> K["Tạo vm.$el và mount vào DOM"]
  K --> L[mounted]
  L --> M{Data thay đổi?}
  M -- Có --> N[beforeUpdate]
  N --> O["Virtual DOM re-render & patch"]
  O --> P[updated]
  P --> M
  M -- vm.$destroy gọi --> Q[beforeDestroy]
  Q --> R["Teardown: watchers, child components, event listeners"]
  R --> S[destroyed]
```

### Hook nào dùng cho việc gì?

| Hook            | Dùng khi                                                         |
| --------------- | ---------------------------------------------------------------- |
| `created`       | Gọi API để lấy data, khởi tạo state ban đầu                      |
| `mounted`       | Thao tác DOM, init jQuery plugins, init chart libraries          |
| `beforeDestroy` | Clear setTimeout/setInterval, unsubscribe events                 |
| `updated`       | Thực thi code sau khi DOM đã cập nhật (dùng `$nextTick` nếu cần) |

***

## Không dùng Arrow Function trong Hooks

```js
// SAI — arrow function không có `this` riêng
new Vue({
  created: () => {
    console.log(this.a) // this = undefined hoặc window
  }
})

// SAI trong $watch
vm.$watch('a', newValue => this.myMethod())

// ĐÚNG — dùng regular function
new Vue({
  created() {
    console.log(this.a) // this = Vue instance
  }
})

// ĐÚNG trong $watch
vm.$watch('a', function (newValue, oldValue) {
  this.myMethod() // this = Vue instance
})
```

Lý do: arrow function không có `this` riêng nên `this` bind vào scope cha (`window` hoặc `undefined` ở strict mode), gây lỗi `Cannot read property of undefined`.

***

## Vue Instance trong AEM

### Mount Vue vào element HTL render ra

HTL render server-side trước nên Vue instance chỉ cần mount vào DOM đã có:

```html
<!-- HTL component: myComponent.html -->
<div data-sly-use.model="com.example.MyModel"
     id="my-app"
     data-items="${model.itemsJson}">
</div>
```

```js
// clientlib/js/myComponent.js
(function() {
  var el = document.getElementById('my-app')
  if (!el) return // guard: element không tồn tại

  var vm = new Vue({
    el: el,
    data: function() {
      return {
        // Parse JSON data từ HTL thông qua data attribute
        items: JSON.parse(el.dataset.items || '[]'),
        loading: false
      }
    },
    created: function() {
      console.log('[MyApp] Vue instance created, items:', this.items.length)
    },
    mounted: function() {
      console.log('[MyApp] Mounted to DOM element:', this.$el.id)
    }
  })
})()
```

### Dùng $nextTick sau khi cập nhật data

```js
var vm = new Vue({
  el: '#app',
  data: { items: [] },
  methods: {
    loadItems: function() {
      this.items = fetchItems() // data thay đổi

      // DOM chưa cập nhật tại đây, query có thể sai
      console.log(this.$el.querySelectorAll('.item').length)

      // Đợi DOM update xong
      this.$nextTick(function() {
        console.log(this.$el.querySelectorAll('.item').length) // chính xác
      })
    }
  }
})
```

### Expose Vue instance ra window để debug

```js
// Chỉ dùng trong development!
var vm = new Vue({ /* ... */ })

if (process.env.NODE_ENV !== 'production') {
  window.__vm = vm
}
```

### Cleanup đúng cách khi AEM SPA navigate

Trong AEM SPA hoặc khi component bị remove khỏi DOM, luôn destroy Vue instance:

```js
var vm = new Vue({ el: '#app' /* ... */ })

// Khi component bị unmount/remove
function cleanup() {
  if (vm) {
    vm.$destroy()
    vm = null
  }
}
```

***

## Tham khảo API

- [`new Vue(options)`](https://v2.vuejs.org/v2/api/#Options-Data) — Full options reference
- [Instance Properties](https://v2.vuejs.org/v2/api/#Instance-Properties)
- [Instance Methods / Data](https://v2.vuejs.org/v2/api/#Instance-Methods-Data)
- [Instance Methods / Lifecycle](https://v2.vuejs.org/v2/api/#Instance-Methods-Lifecycle)
