跳至主要內容
Skip to content

Vue 核心觀念:MVVM、宣告式渲染與單向資料流

如果面試官問:「你怎麼理解 Vue?」很多人會回答:「Vue 是一個 MVVM 框架。」這句話不算錯,但如果只停在這裡,就很容易被追問打穿。

比較好的回答是:Vue 借用了 MVVM 的心智模型,讓開發者把重點放在狀態與畫面的關係上,而不是手動操作 DOM。當資料改變時,Vue 會透過響應式系統追蹤依賴,並自動安排畫面更新。


一、 必考觀念

1. MVVM 是什麼?

MVVM 是 Model、View、ViewModel 的縮寫:

角色職責在 Vue 中的大致對應
Model應用資料與業務狀態refreactive、Pinia store、API 回傳資料
View使用者看到的畫面Template、DOM、CSS
ViewModel連接資料與畫面的中介層Vue 元件實例、setup 回傳狀態、響應式系統

用一句話講:MVVM 的核心是讓資料狀態與畫面呈現建立自動同步關係。

在傳統 DOM 操作裡,我們常會這樣寫:

javascript
const count = 1
document.querySelector('#count').textContent = count

count 改變時,我們還要記得再操作一次 DOM:

javascript
count = count + 1
document.querySelector('#count').textContent = count

在 Vue 裡,我們描述的是資料和畫面的關係:

vue
<template>
  <button @click="count++">
    {{ count }}
  </button>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const count = ref(1)
</script>

開發者不需要直接告訴 DOM 怎麼改。只要 count 改變,Vue 就會負責把畫面更新到正確狀態。

2. Vue 是嚴格的 MVVM 嗎?

更精準地說,Vue 是 受到 MVVM 啟發的前端框架,不必把它硬套成教科書裡的 MVVM。

原因是 Vue 元件同時包含 Template、狀態、事件處理與部分邏輯,不是完全把 View、ViewModel、Model 分成三個獨立層。實務上,我們使用 MVVM 是為了建立心智模型:

  • 資料是畫面的來源。
  • 畫面由資料推導出來。
  • 使用者操作會觸發事件,事件再修改資料。
  • 資料變更後,框架負責更新畫面。

這比「Vue 就是 MVVM」更像一個能在面試中站得住的答案。

3. 宣告式渲染是什麼?

宣告式渲染的意思是:你描述「畫面應該長什麼樣」,而不是一步一步命令瀏覽器「現在要改哪個 DOM」。

例如:

vue
<template>
  <p v-if="isLogin">歡迎回來,{{ username }}</p>
  <button v-else @click="login">登入</button>
</template>

這段程式碼描述的是:

  • isLogintrue 時,顯示歡迎訊息。
  • isLoginfalse 時,顯示登入按鈕。

至於中間 DOM 怎麼建立、怎麼移除、怎麼更新文字節點,是 Vue 內部處理的事情。

4. 單向資料流是什麼?

Vue 的元件資料流通常是單向的:父元件透過 Props 把資料傳給子元件,子元件透過 Emit 通知父元件發生了什麼事。

vue
<!-- Parent.vue -->
<template>
  <UserCard
    :user="user"
    @rename="handleRename"
  />
</template>
vue
<!-- UserCard.vue -->
<script setup lang="ts">
defineProps<{
  user: {
    name: string
  }
}>()

const emit = defineEmits<{
  rename: [name: string]
}>()

function submitName(name: string) {
  emit('rename', name)
}
</script>

這樣做的好處是資料來源清楚。當畫面出錯時,比較容易追蹤狀態是從哪裡來、又是在哪裡被改掉。

5. 雙向綁定和單向資料流衝突嗎?

不衝突。

v-model 看起來像雙向綁定,但在元件上本質是語法糖。它可以展開成:

vue
<CustomInput
  :model-value="title"
  @update:model-value="title = $event"
/>

也就是:

  • 父元件把 title 傳給子元件。
  • 子元件發出 update:model-value 事件。
  • 父元件收到事件後更新自己的狀態。

所以 v-model 並不是讓子元件直接任意修改父元件狀態,而是把「傳值」與「更新事件」包成比較好寫的形式。


二、 追問題庫

Q1:Vue 為什麼不建議子元件直接修改 Props?

因為 Props 的來源在父元件。子元件如果直接修改 Props,會讓資料流變得混亂,也可能在父元件重新渲染時被覆蓋。

正確做法通常有三種:

  • 子元件透過 emit 通知父元件更新。
  • 子元件建立本地狀態,將 Props 當作初始值。
  • 多層共享狀態時,抽到 Pinia 或 Composable。

Q2:Vue 的資料改變後,畫面是同步更新嗎?

不是完全同步。

Vue 會把多次狀態變更放進更新佇列,等同一輪事件循環中的同步程式執行完,再批次更新 DOM。這能避免每次資料一改就立刻重繪,減少不必要的 DOM 操作。

如果你需要等 DOM 更新完成後再讀取畫面狀態,可以使用 nextTick

typescript
import { nextTick, ref } from 'vue'

const visible = ref(false)

async function openPanel() {
  visible.value = true
  await nextTick()
  // 這裡再讀取 DOM 尺寸會比較可靠
}

Q3:MVVM 和 MVC 差在哪?

簡化理解:

模式重點
MVCController 接收操作,更新 Model,再決定 View 如何變化
MVVMViewModel 連接資料與畫面,資料變化會自動反映到 View

在前端框架裡,不必過度糾結名詞邊界。面試時更重要的是說清楚:Vue 透過響應式系統與宣告式渲染,把「狀態變更」到「畫面更新」這段流程自動化。

Q4:Vue 和 jQuery 最大差異是什麼?

jQuery 的核心思維是「找到 DOM,然後修改它」。Vue 的核心思維是「描述狀態和畫面的關係,讓框架維護 DOM」。

例如列表更新:

javascript
// jQuery 思維:手動組 HTML、清空、插入
$('#list').html(items.map(item => `<li>${item.name}</li>`).join(''))
vue
<!-- Vue 思維:描述列表如何由資料產生 -->
<li v-for="item in items" :key="item.id">
  {{ item.name }}
</li>

當資料與互動變複雜時,Vue 的資料驅動畫面會比手動維護 DOM 更穩定。

Q5:Vue 的核心能力可以怎麼總結?

可以用四句話回答:

  1. Vue 用宣告式渲染描述資料與畫面的關係。
  2. Vue 用響應式系統追蹤狀態依賴。
  3. Vue 在資料變更後自動安排畫面更新。
  4. Vue 用元件化與單向資料流管理複雜 UI。

三、 實作題

題目:把命令式 DOM 操作改成 Vue 寫法

假設有一段傳統寫法:

html
<button id="toggle">切換</button>
<p id="message">目前關閉</p>
javascript
let enabled = false

document.querySelector('#toggle').addEventListener('click', () => {
  enabled = !enabled
  document.querySelector('#message').textContent = enabled
    ? '目前開啟'
    : '目前關閉'
})

改成 Vue 時,可以這樣寫:

vue
<template>
  <button @click="enabled = !enabled">切換</button>
  <p>{{ message }}</p>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'

const enabled = ref(false)

const message = computed(() => {
  return enabled.value ? '目前開啟' : '目前關閉'
})
</script>

這裡的重點不是少寫幾行,而是責任改變了:

原本 DOM 寫法Vue 寫法
手動監聽 DOM 事件Template 綁定事件
手動保存狀態ref 保存狀態
手動更新文字節點computed 推導畫面文字
開發者維護 DOMVue 維護 DOM

面試時可以補一句:當畫面越複雜,這種資料驅動的寫法越容易維護,因為 UI 是從狀態推導出來的,不是散落在各處的 DOM 操作結果。


四、 資深視角

1. 不要把 MVVM 當成萬用答案

資淺回答「Vue 是 MVVM 框架」通常可以過關,但資深一點的回答應該補上限制:

  • Vue 不需要嚴格遵守傳統 MVVM 分層。
  • Vue 元件內可能同時存在 View 與 ViewModel 的責任。
  • 大型專案更重要的是元件邊界、狀態歸屬與資料流一致性。

所以 MVVM 是入門模型,不是架構銀彈。

2. 單向資料流是維護性的基礎

小型頁面裡,直接改資料通常感覺最快。但當元件層級變深、多人協作增加、狀態來源變多時,真正麻煩的是「不知道誰改了資料」。

資深工程師在設計 Vue 應用時,通常會先確認:

  • 這個狀態應該屬於頁面、元件、Composable,還是全域 store?
  • 子元件是純展示,還是可以管理自己的互動狀態?
  • v-model 是讓 API 更好用,還是掩蓋了資料來源?
  • 狀態更新是否能被追蹤、測試與回放?

3. 宣告式不代表不用理解 DOM

Vue 幫我們處理大多數 DOM 更新,但不代表可以完全忽略瀏覽器行為。像是焦點控制、滾動位置、動畫、第三方套件整合、效能瓶頸,都還是需要理解真實 DOM。

好的 Vue 工程師不是只會寫 Template,而是知道什麼時候讓框架接手,什麼時候需要回到 DOM、事件循環與渲染成本來思考。


總結

這題的面試重點不是背出 MVVM 三個單字,而是說清楚 Vue 的核心心智模型:

核心觀念面試回答重點
MVVMVue 借用 MVVM 思想,讓資料與畫面建立自動同步關係
宣告式渲染描述畫面應該長什麼樣,而不是手動操作 DOM
響應式更新狀態改變後,Vue 追蹤依賴並安排畫面更新
單向資料流Props 往下傳,事件往上通知,資料來源更清楚
v-model元件上的雙向綁定本質是 Props 加 Emit 的語法糖

一句比較完整的面試回答可以是:

Vue 是一個以資料驅動畫面的前端框架。它借用了 MVVM 的心智模型,讓開發者透過 Template 描述狀態與畫面的關係,再由響應式系統追蹤依賴並更新 DOM。實務上,我會把 Vue 理解成宣告式渲染、響應式狀態和元件化資料流的組合,而不是只把它簡化成 MVVM。

延伸閱讀