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, and this page is available as Markdown at https://nhanbe.id.vn/frontend/vue2.7/instance.md.

Understanding the Vue 2.7 instance lifecycle, reactive state, and options.

Vue Instance

Tạo instance

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.

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:

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):

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:

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

new Vue({ el: '#app', data: obj })
<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.

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

PropertyMô tả
vm.$dataObject data của instance
vm.$elDOM element mà instance mount vào
vm.$optionsOptions object được dùng khi khởi tạo
vm.$parentInstance cha (nếu có)
vm.$rootRoot instance của cây component
vm.$childrenArray các component con trực tiếp
vm.$refsObject chứa các DOM elements / child components được đánh dấu ref
vm.$slotsObject chứa nội dung slot
vm.$attrsAttributes không được khai báo là props

Các instance methods quan trọng

MethodMô 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

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

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

HookDùng khi
createdGọi API để lấy data, khởi tạo state ban đầu
mountedThao tác DOM, init jQuery plugins, init chart libraries
beforeDestroyClear setTimeout/setInterval, unsubscribe events
updatedThự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

// 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ó:

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

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

// 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:

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

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

Tham khảo API