iT邦幫忙

2021 iThome 鐵人賽

DAY 11
0
Modern Web

不只懂 Vue 語法:Vue.js 觀念篇系列 第 11

不只懂 Vue 語法:如何使用 v-model 實現父子元件傳遞資料?

問題回答

所謂父子元件傳遞資料,就是透過使用 propsemits 來完成。而 v-model 只是結合使用 propsemits 的語法糖。例如在 input 綁上 v-model 時,背後其實是監聽 input 事件,當 input 事件觸發,就更新你在 v-model 所指定的值。

套用以上原理,假設我要把子元件裏的 input 與父元件的資料實現雙向綁定。做法就是:

  • 在父元件,為子元件綁上 v-model="你要更新的值"
  • 回到子元件裏,以 props 傳入在父元件的資料。並且監聽 input 事件以及設定 emit。當 input 事件觸發,就更新在父元件的資料。

在第二步提到的 props,Vue 3 會使用 modelValue ,Vue 2 則需要我們在子元件建立 model 屬性來完成。

另外,非常建議大家參考這部分的官方文件,它簡單說明了 Vue 2 和 Vue 3的做法。

以下會再詳細解說。

v-model 只是一個語法糖

我們可以在 input、textarea、select 這些元素上使用 v-model,雙向綁定資料和用戶輸入的內容。以 input 為例:

<input v-model="text">
data () {
  return {
    text: 'v-model 綁定的資料'
  }
}

仔細想想,這個寫法背後的原理就是:

  1. 觸發 input 事件。
  2. 把 input 輸入欄裏的資料寫入 Vue data 裏的 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可以實現這功能,那麼你最快想到的方法可能就是,使用 propsemit,做法像下面這樣:

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>

以上就是直接使用 propsemit 的方法來完成。透過觸發 input 事件來更新在父元件的資料。

程式碼示範:
https://codesandbox.io/s/v-model-zhi-shi-yong-props-he-emit-de-xie-fa-dsl3w?file=/src/components/Child.vue

Vue 3 裏使用 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

Vue 2 使用 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。

完整程式碼示範

https://codesandbox.io/s/vue-2-shi-yong-v-model-shi-xian-fu-zi-yuan-jian-shuang-xiang-bang-ding-j6rne?file=/src/App.vue

除了 v-model,Vue 2 還可以用 .syncv-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

總結

  • 雙向綁定是由 props 和 emit 來實現。即是觸發事件、監聽事件、之後更新指定的值。
  • 在 Vue 3 或 Vue 2,均可使用 v-model來實現父子元件雙向綁定。
  • Vue 3 使用 modelValue 來指定父元件 v-model 所綁定的 props。相反,Vue 2 裏需要在子元件建立 model 屬性來指定 v-model要綁定的props
  • Vue 2 可使用 .syncv-bind 來完成 v-model 所實現的效果。

參考資料

重新認識 Vue.js - 2-2 元件之間的溝通傳遞
Vue Component 也可以用 v-model
Vue.js v-model overview


上一篇
不只懂 Vue 語法:什麼是單向資料流和雙向綁定?
下一篇
不只懂 Vue 語法:如何用 event bus 或 mitt 實現跨元件傳遞資料?
系列文
不只懂 Vue 語法:Vue.js 觀念篇31

尚未有邦友留言

立即登入留言