props和emits之間的組件通信

數據流向

  • props:父組件 → 子組件(數據下行)
  • emit:子組件 → 父組件(事件上行)

核心機制

  • props:父組件通過屬性傳遞數據給子組件
  • emit:子組件通過事件向父組件發送消息

設計模式

單向數據流

父組件 --props--> 子組件 --emit--> 父組件

完美配合

  • props 傳遞狀態數據
  • emit 通知狀態變更
  • 共同實現可複用、可維護的組件化架構

這就是Vue組件間通信的基礎模式!



分項解釋

分項解釋1 - Props

在Vue中,props(屬性) 是父組件向子組件傳遞數據的自定義屬性。

主要特點:

  • 單向數據流:數據只能從父組件流向子組件,子組件不能直接修改props
  • 可配置性:可以定義類型驗證、默認值、是否必需等
  • 組件通信:實現父組件與子組件的數據傳遞

示例:

<!-- 子組件 -->
<script setup>
defineProps({
  title: String,
  count: {
    type: Number,
    default: 0
  }
})
</script>

<!-- 父組件使用 -->
<ChildComponent title="Hello" :count="5" />

props使得Vue組件可以複用並接收不同的數據,是組件化開發的核心概念之一。


分項解釋2 - Emits

在Vue中,emit(發射) 是子組件向父組件傳遞消息的方法。

主要特點:

  • 子傳父:子組件通過事件通知父組件
  • 觸發父組件方法:父組件可以監聽子組件發出的事件並執行相應邏輯
  • 可傳遞數據:可以攜帶數據傳遞給父組件

示例:

<!-- 子組件 -->
<script setup>
// 1. 定義emit函數(獲取發射事件的能力) - emit(事件名稱, 傳遞的數據)
const emit = defineEmits(['update'])

function handleClick() {
  // 2. 使用emit函數來發射事件
  emit('update', newValue)  // 這裏是調用上面定義的emit函數
}
</script>

<!-- 父組件 -->
<ChildComponent @update="handleUpdate" />

emit與props配合,實現了Vue組件間的雙向通信機制。


分項解釋3 - @update和@input

相同點

  • 都是事件監聽語法
  • 都使用 @ 符號作為簡寫
  • 都用於響應某個動作

不同點

@input

  • 原生DOM事件(瀏覽器內置)
  • 瀏覽器自動觸發(用户輸入時)
  • 主要用於表單元素
<input @input="handleInput" />
<!-- 用户輸入時自動觸發 -->

@update

  • 自定義組件事件
  • 需要子組件手動觸發(通過 emit
  • 用於組件間通信
<!-- 子組件內部 -->
emit('update', data)

<!-- 父組件使用 -->
<ChildComponent @update="handleUpdate" />

總結

  • @input:監聽瀏覽器原生輸入事件
  • @update:監聽子組件自定義事件

一個是"被動監聽",一個是"主動通知"的關係!


分項解釋4 - @update:modelValue

這是Vue中v-model的自定義實現語法:

  • 用於實現自定義組件的v-model
  • 是Vue 3中v-model的底層機制

傳統v-model等價關係

<!-- 這兩種寫法是等價的 -->
<CustomInput v-model="searchText" />

<CustomInput
  :modelValue="searchText"
  @update:modelValue="newValue => searchText = newValue"
/>

完整示例

子組件

<script setup>
// 定義組件接收的屬性:一個名為modelValue的prop(來自父組件v-model綁定的值)
defineProps(['modelValue'])

// 定義組件可以發射的事件:一個名為update:modelValue的自定義事件
defineEmits(['update:modelValue'])

// 處理輸入框輸入事件的函數
function updateValue(event) {
  // 發射update:modelValue事件,將輸入框的新值傳遞給父組件
  // event.target.value是輸入框當前的值
  emit('update:modelValue', event.target.value)
}
</script>

<template>
  <!-- 
    輸入框組件:
    :value="modelValue" - 將輸入框的值綁定到從父組件傳來的modelValue
    @input="updateValue" - 當輸入框有輸入時,觸發updateValue函數
  -->
  <input 
    :value="modelValue"
    @input="updateValue"
  />
</template>

父組件

<CustomInput v-model="text" />
<!-- 等價於 -->
<CustomInput 
  :modelValue="text"
  @update:modelValue="newValue => text = newValue"
/>

數據流向説明:

父組件 v-model="text"
    → 子組件 :modelValue="text" (props傳入)
    → 用户輸入 → @input觸發 → emit發射事件
    → 父組件 @update:modelValue接收 → 更新text

總結

@update:modelValue 是v-model的事件監聽部分,用於接收子組件的數據更新通知!



Vue3 最新的父子通信(雙向綁定)方式

Vue 3.4+ 官方文檔 推薦使用 defineModel,大大簡化了雙向綁定的實現。

新舊對比

舊方式(複雜)

<script setup>
defineProps(['modelValue'])
defineEmits(['update:modelValue'])

function updateValue(event) {
  emit('update:modelValue', event.target.value)
}
</script>

<template>
  <input 
    :value="modelValue"
    @input="updateValue"
  />
</template>

新方式(簡潔)

<script setup>
const modelValue = defineModel()
</script>

<template>
  <input v-model="modelValue" />
</template>

defineModel 的優勢

  1. 一行代碼搞定:替代了 defineProps + defineEmits + 更新函數
  2. 直接使用 v-model:在模板中可以直接用 v-model 綁定
  3. 類型安全:更好的 TypeScript 支持
  4. 更直觀:邏輯更清晰易懂

使用方法

基本用法

<script setup>
const value = defineModel()
</script>

<template>
  <input v-model="value" />
</template>

帶默認值和類型

<script setup>
const count = defineModel({ default: 0, type: Number })
</script>

結論

Vue 3.4+ 強烈推薦使用 defineModel,不再需要手動拆解 props 和 emit,代碼更加簡潔優雅!

這是 Vue 雙向綁定演進的重要改進!