所謂父子元件傳遞資料,就是透過使用 props 和 emits 來完成。而 v-model 只是結合使用 props 和 emits 的語法糖。例如在 input 綁上 v-model 時,背後其實是監聽 input 事件,當 input 事件觸發,就更新你在 v-model 所指定的值。
套用以上原理,假設我要把子元件裏的 input 與父元件的資料實現雙向綁定。做法就是:
v-model="你要更新的值"。在第二步提到的 props,Vue 3 會使用 modelValue ,Vue 2 則需要我們在子元件建立 model 屬性來完成。
另外,非常建議大家參考這部分的官方文件,它簡單說明了 Vue 2 和 Vue 3的做法。
以下會再詳細解說。
我們可以在 input、textarea、select 這些元素上使用 v-model,雙向綁定資料和用戶輸入的內容。以 input 為例:
<input v-model="text">
data () {
  return {
    text: 'v-model 綁定的資料'
  }
}
仔細想想,這個寫法背後的原理就是:
text 裏。所以,v-model 其實是由監聽 input 事件,以及綁定與更新數值來實現:
<input :value="text" @input="text = $event.target.value"/>
v-model 可以用在父子元件資料傳遞?神奇的是,以上的寫法也可以應用在父子元件,實現雙向綁定資料。
假設目前有兩個元件,父子關係如下:
App.vue
    Child.vue
App.vue(父元件):
data() {
    return {
      msg: "父層 msg",
    };
}
Child.vue (子元件):
<input />
我們想要把父元件的 msg 與 Child 的 input 實現雙向綁定。即是說,只要使用者在 Child 子元件裏的 input 一輸入內容,父元件的 msg 也會隨之而更新。

如果你不知道原來 v-model可以實現這功能,那麼你最快想到的方法可能就是,使用 props 和 emit,做法像下面這樣:
App.vue:
<template>
  {{ msg }}
  <Child :childProps="msg" @input-sth="msg = $event"/>
</template>
<script>
import Child from "./components/Child.vue";
export default {
  name: "App",
  components: {
    Child
  },
  data() {
    return {
      msg: "父層 msg",
    };
  },
};
</script>
Child.vue
<template>
  <input type="text" :value="childProps" @input="$emit('input-sth', $event.target.value)">
</template>
<script>
export default {
  props: ['childProps']
}
</script>
以上就是直接使用 props 和 emit 的方法來完成。透過觸發 input 事件來更新在父元件的資料。
v-model 雙向綁定父子元件的資料然而,用 v-model 來寫就會更簡潔。但要注意的是,props 必須是 modelValue,emit 事件的名稱必須是 update:modelValue。
以下先示範 v-model的寫法,得出的結果是一樣:
App.vue
<template>
  {{ msg }}
  <Child v-model="msg"/>
</template>
Child.vue
<template>
  <input
    type="text"
    :value="modelValue"
    @input="$emit('update:modelValue', $event.target.value)"
  />
</template>
<script>
export default {
  props: ["modelValue"],
};
</script>
先看看 Child 的部分,你會發現這裏做的事,跟再前一個例子所做的事是相同。只不過是改了 props 和 emit 事件的名字。
之後,按以上寫法,這裏的 v-model:
<Child v-model="msg"/>
等於:
<Child :modelValue="msg" @update:modelValue="msg = $event" />
如果想自定義 props 名稱,不用 modelValue的話,就直接在  v-model 後面定義:
<Child v-model:customProps="msg" />
https://codesandbox.io/s/vue-3-shi-yong-props-emit-shi-xian-v-model-ekk42?file=/src/App.vue
v-model 的做法在 Vue 2 同樣可以使用 v-model 來完成父子元件的雙向綁定。但在子元件的 data 裏,需要加入 model 屬性來指定 v-model 所綁定的 props。
App(父元件):
<Child v-model="msg" />
data() {
    return {
      msg: "這是父層 Msg",
    };
}
Child (子元件):
<div>
    <input
      :value="childMsg"
      @input="$emit('input', $event.target.value)"
    />
</div>
export default {
  model: {
    prop: "childMsg",
  },
  props: {
    value: String,
    childMsg: {
      type: String,
      default: "default msg",
    },
  },
};
Vue 3 只需綁定 modelValue 即可。但在 Vue 2,以上示範了要自行加入 model 屬性來指定父層 v-model 所綁定的 props。
v-model,Vue 2 還可以用 .sync 和 v-bind除了以上提到的 v-model 方法,.sync 結合 v-bind 做法也能實現同樣效果。還記得以上提到,v-model 其實只是由觸發事件、綁定與更新組成嗎?而 .sync 就是實現這兩個效果的縮寫。
試試用這語法來實現同樣的效果:
Child(子元件):
<div>
    <!-- 官方建議 update:myPropName 這格式的寫法 -->
    <input
      :value="childMsg"
      @input="$emit('update:childMsg', $event.target.value)"
    />
</div>
export default {
  props: ["childMsg"],
};
App(父元件):
<Child :childMsg.sync="parentMsg" />
data() {
    return {
      parentMsg: "這是父層 msg"
    };
},
注意,這裏的寫法:
<Child :childMsg.sync="parentMsg" />
即等於:
<Child :childMsg="parentMsg" @update:childMsg="parentMsg = $event" />
但謹記:Vue 3 已移除 .sync 語法,只能在 Vue 2 使用。
https://codesandbox.io/s/vue-2-sync-yong-fa-xp4fk?file=/src/App.vue
v-model來實現父子元件雙向綁定。modelValue 來指定父元件 v-model 所綁定的 props。相反,Vue 2 裏需要在子元件建立 model 屬性來指定 v-model要綁定的props。.sync 和 v-bind 來完成 v-model 所實現的效果。重新認識 Vue.js - 2-2 元件之間的溝通傳遞
Vue Component 也可以用 v-model
Vue.js v-model overview