不只懂 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


<<:  RISC V::RV32I 指令介绍

>>:  【Day6】:GPIO输入输出(中)

Day 21 「事有经重缓急」Clean Architecture 简易入门

古语有云:「岁有凶穰;故谷有贵贱;令有缓急;故物有轻重。」旨在告诉後人,做任何事情,一定要先搞清楚状...

[Angular] Day3. angular.json

在上一篇中介绍了什麽是 Angular CLI 与他可以提供许多方便功能,不过只是大概介绍他的用法与...

Day9-Kind:你叫我做,我就要去做吗?Kind指令介绍

kind指令介绍 在上一章我们介绍了如何安装kind,既然安装完了,那就该知道kind支援哪些指令。...

Day 29:管理

前言 我们经常会说换位思考,也就是说,身为工程师,如果能清楚知道管理阶级的想法,就能聪明有效率的做事...

30天学会Python: Day 9- 程序码也能取名字

自订函式(User Definded Function) 自己定义函式有几个优点: 增加程序码的可读...