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/articles/mvvm.md.

Understanding MVVM design pattern principles and implementation in web frontend applications.

MVVM

MVVM (Model -View -ViewModel)

  • Model: dữ liệu (thường nằm trong data)
  • View: Giao diện HTML
  • ViewModel: Chính là Vue instance - cầu nối giữa Model và View.

Tại sao trong docs lại dùng biến vm

Trong tài liệu Vue, vm như 1 biến đại diện minh họa cho Vue instace.

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

Ví dụ:

// Cách 1: Đặt tên tùy ý
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

// Cách 2: Không gán vào biến nào cả (thường dùng trong main.js)
new Vue({
  el: '#app',
  render: h => h(App)
})

// Cách 3: Trong component (phổ biến nhất)
export default {
  data() {
    return {
      count: 0
    }
  },
  methods: {
    increment() {
      this.count++ // this chính là Vue instance (tương đương vm trong docs)
    }
  }
}

Minh hoạ cơ chế reactive của Vue

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

// Vue "proxy" các thuộc tính từ data vào vm
// Nên vm.a thực chất là truy cập đến data.a
vm.a = 2      // Thay đổi vm.a
data.a // => 2 (data cũng thay đổi theo)

data.a = 3    // Thay đổi data.a
vm.a // => 3  (vm.a cũng thay đổi theo)
  • Khi tạo Vue instance, Vue sử dụng Object.defineProperty (trong Vue2) để tạo getter/setter cho các thuộc tính vm.a thực chất không phải là một thuộc tính riêng, mà là "cửa ngõ" để truy cập data.a
  • Khi bạn thay đổi vm.a, Vue biết và cập nhật data.a, sau đó kích hoạt re-render view

Dùng cách nào để mount

1. Dùng el: "#app" (khai báo lúc tạo instance)

// Cách 1: Dùng el trong options
var vm = new Vue({
  el: '#app',  // Tự động mount ngay khi tạo
  data: {
    message: 'Hello'
  }
})
// => Vue instance được tạo và mount ngay lập tức vào #app

2. Dùng .$mount('#app') (gọi method sau khi tạo)

// Cách 2: Dùng $mount() method
var vm = new Vue({
  data: {
    message: 'Hello'
  }
})
// Vue instance được tạo nhưng CHƯA mount
vm.$mount('#app')  // Mount sau

Sự khác biệt về timing (thời điểm thực thi)

Đặc điểmel: "#app".$mount('#app')
Thời điểm mountNgay khi khởi tạo Vue instanceSau khi Vue instance đã được tạo
Có thể delay không?KhôngCó
Dùng với SSR?Không phù hợpPhù hợp

Ví dụ thực tế cho thấy sự khác biệt

// === Cách 1: el - Mount ngay lập tức ===
var vm = new Vue({
  el: '#app',
  data: { count: 0 },
  created() {
    console.log('Created!')
    console.log(this.$el) // undefined - vì chưa mount xong
  },
  mounted() {
    console.log('Mounted!')
    console.log(this.$el) // <div id="app">...</div>
  }
})
// Created! và Mounted! chạy ngay

// === Cách 2: $mount - Có thể delay ===
var vm = new Vue({
  data: { count: 0 },
  created() {
    console.log('Created!')
  }
})

// Làm gì đó trước khi mount...
console.log('Chuẩn bị mount...')

// Mount sau
vm.$mount('#app')  // Lúc này mounted() mới chạy

Khi nào nên dùng cách nào?

<details> <summary><strong>✅ Dùng el: "#app" khi nào?</strong></summary>

Dùng khi bạn muốn mount ngay lập tức và không cần làm gì trước khi mount.

Phù hợp cho:

  • Ứng dụng Vue thuần túy (không kèm thư viện khác)
  • Code đơn giản, trực tiếp
  • Không cần kiểm tra điều kiện trước khi mount
// Trường hợp phổ biến
new Vue({
  el: '#app',
  data: { /* ... */ },
  methods: { /* ... */ }
})

</details>

<details> <summary><strong>✅ Dùng .$mount('#app') khi nào?</strong></summary>

Dùng khi bạn cần kiểm soát thời điểm mount hoặc cần làm gì đó trước khi mount.

Phù hợp cho:

  1. Kết hợp với các thư viện khác (như Vue Router, Vuex cần khởi tạo trước):
// main.js trong Vue CLI project
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

// Tạo instance nhưng chưa mount
const app = new Vue({
  router,
  store,
  render: h => h(App)
})

// Có thể làm gì đó ở đây (ví dụ: kiểm tra auth)
console.log('App created, preparing to mount...')

// Mount sau
app.$mount('#app')
  1. Server-Side Rendering (SSR):
// Trong SSR, bạn tạo app nhưng mount ở client
export default function createApp() {
  const app = new Vue({
    // ...
  })
  return { app, router }
}
// Sau đó mount ở client
  1. Kiểm tra điều kiện trước khi mount:
const app = new Vue({
  data: { /* ... */ }
})

if (someCondition) {
  app.$mount('#app')
} else {
  app.$mount('#fallback-app')
}

</details>

Trong thực tế code Vue2 nên dùng cách nào?

Khuyến nghị theo trường hợp:

1. Vue CLI project (phổ biến nhất):

// main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')  // Dùng $mount

👉 Lý do: Vue CLI thường dùng cách này vì tính nhất quán và dễ mở rộng.

2. Code đơn giản, không dùng build tool:

// index.js hoặc script trực tiếp
new Vue({
  el: '#app',  // Dùng el
  data: {
    message: 'Hello Vue!'
  }
})

👉 Lý do: Đơn giản, trực tiếp, dễ hiểu.

3. Khi cần kiểm soát thời điểm mount:

const app = new Vue({
  // options
})

// Chờ API, hoặc kiểm tra điều kiện
fetch('/api/config')
  .then(() => {
    app.$mount('#app')
  })

Tóm lại

Tiêu chíNên dùng
Code đơn giản, mount ngayel: "#app"
Vue CLI project.$mount('#app')
Cần delay hoặc kiểm tra trước khi mount.$mount('#app')
SSR hoặc kết hợp nhiều thư viện.$mount('#app')

Lời khuyên: Nếu làm việc với Vue CLI hoặc dự án thực tế, hãy quen với .$mount('#app'). Còn nếu viết code đơn giản trực tiếp, el: "#app" cũng hoàn toàn ổn.

Cả hai cách đều hoạt động giống nhau về kết quả cuối cùng, chỉ khác nhau về thời điểm mount thôi. 😊