添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

在Vue开发中,父子组件之间经常需要进行数据传递和交互。使用v-model指令可以很容易地在父子组件之间实现数据的双向绑定。本文将详细介绍Vue父子组件使用v-model的方法和注意事项。

最近有一个小发现,Vue3.3版本开始, v-model 这个语法糖有了些许调整;这里就来说一下。

v-model

要解释什么是 v-model ,可能要先解释一下Vue中的子父组件传值:

  • props : 子组件用来接收从父组件传递的数据;在Vue中,父组件到子组件的数据传递可以通过 props 实现的。父组件可以通过 props 向下传递数据给子组件,子组件通过定义 props 选项来接收来自父组件的数据。这种数据流是单向的,意味着子组件不能直接修改 props 中的数据,因为这将导致父子组件间的数据状态不一致性,Vue中会发出警告。
  • emit : 子组件用来向父组件发送消息;当子组件需要将数据变化通知父组件时,它可以使用 $emit 来触发一个事件,并将数据作为事件的参数传递给父组件。父组件监听这个事件,并在回调函数中处理接收到的数据。通常用于子组件向父组件发送信号告知某些动作已经发生,或者数据需要更新。
  • 在这样的机制下,Vue提供了 v-model 语法糖指令来简化双向数据绑定,也就是在子组件内,使用特定的 props 属性和 emit 事件,就可以 使用 v-model 简化父组件内的传值流程,进而实现数据双向绑定

    只是在Vue2、Vue3.0~Vue3.3和Vue3.3+内有所不同。

    Vue2

    在Vue2中,使用 v-model ,会自动使用名称为 value 的props属性,和使用名称为 input 的监听事件。进而使用这个特性,封装自己的组件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <template>
    <input
    v-model="localMessage"
    />
    </template>

    <script>
    export default {
    props: ['value'],

    computed: {
    // 使用计算属性,实现子组件内的内容更新会自动更新父组件
    localMessage: {
    get() {
    return this.value
    },
    set(newValue) {
    this.$emit('input', newValue)
    }
    }
    }
    }
    </script>

    这样父组件内部,可以有原本 <child :value="username" @input="username=$event" /> ,简化为 <child v-model="username" />

    当然,有些人可能会使用 $refs ,通过父组件调用子组件内部公开的方法来完成数据的同步,比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <template>
    <div>
    <child ref="childComponent" />
    <button @click="callChildMethod">调用子组件方法</button>
    </div>
    </template>

    <script>
    import ChildComponent from './ChildComponent.vue';

    export default {
    components: {
    ChildComponent
    },
    methods: {
    callChildMethod() {
    this.$refs.childComponent.methodName();
    }
    }
    }
    </script>

    但是我认为, $ref 作为获取子组件DOM元素、访问子组件实例的工具钩子;如果是封装组件,特别是表单类别、渲染模块,那么使用 v-model 更优雅,不用考虑还需要 $refs 二次调用的情况。

    Vue3.0~Vue3.3

    但是,Vue3版本开始有所不同。在 [Vue3.0, Vue3.3) 版本之间,为了更好地支持组合式 API 和一致性,Vue 3引入了 modelValue @update 的语法糖,以取代先前的 value @input :

    1
    <input v-model="model"/>

    [Vue3.0, Vue3.3) 之间,等于:

    1
    2
    3
    <input
    v-model="modelValue"
    @update:modelValue="updateModelValue"/>

    具体的使用样例:

    1
    2
    3
    // Parent.vue

    <Child v-model="name" label="Name" />

    子组件实现:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // Child.vue

    <template>
    <input
    class="input"
    type="text"
    :placeholder="props.label"
    :value="props.modelValue"
    v-on:input="updateValue($event.target.value)"
    />
    </template>

    <script setup>

    const props = defineProps({
    modelValue: String
    })

    const emit = defineEmits(['update:modelValue'])

    const updateValue = (value) => {
    emit('update:modelValue', value)
    }
    </script>

    但是,在Vue3.3版本开始(稳定版本应该是Vue3.4)。有有所不同。

    Vue3.4

    Vue版本<3.3 的情况下,就如上文一样, v-model modelValue @update 的语法糖;甚至在Vue2.x时代,是 value @input 的语法糖。

    你当然可以在Vue3.4时候,继续使用 modelValue @update 的语法糖,但是Vue3.4开始,引入一种更优雅的方式: defineModel ,参考自: https://blog.vuejs.org/posts/vue-3-4 :

    1
    2
    3
    4
    5
    6
    Today we're excited to announce the release of Vue 3.4 "🏀 Slam Dunk"!

    This release includes some substantial internal improvements - most notably a rewritten template parser that is 2x faster, and a refactored reactivity system that makes effect triggering more accurate and efficient. It also packs a number of quality-of-life API improvements, including the stabilization of defineModel and a new same-name shorthand when binding props.

    今天,我们很高兴地宣布 Vue 3.4 “🏀灌篮高手”的发布!
    此版本包括一些实质性的内部改进 - 最引人注目的是重写的模板解析器,速度提高了 2 倍,以及重构的反应系统,使效果触发更加准确和高效。它还包含许多生活质量 API 改进,包括绑定 prop 时的 defineModel 稳定性和新的同名速记。

    具体在 setup()内使用,非常简单:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 声明 "modelValue" prop,由父组件通过 v-model 使用
    const model = defineModel()
    // 或者:声明带选项的 "modelValue" prop
    const model = defineModel({ type: String })

    // 在被修改时,触发 "update:modelValue" 事件
    model.value = "hello"

    // 声明 "count" prop,由父组件通过 v-model:count 使用
    const count = defineModel("count")
    // 或者:声明带选项的 "count" prop
    const count = defineModel("count", { type: Number, default: 0 })

    const inc = () => {
    // 在被修改时,触发 "update:count" 事件
    count.value++
    }

    也就是,使用 defineModel ,你就不需要强制在子组件内定义 modelValue @update 了。这个新的语法糖,进一步简化了代码。

    如果你想获取 v-model 的描述,比如定义首字母大写:

    1
    <Child v-model.capitalize="username"/>

    那么用 defineModel 的形式,可以这样:

    1
    2
    3
    4
    5
    6
    7
    8
    // Child.vue
    <script setup>
    const [modelValue, modelModifiers] = defineModel()

    if (modelModifiers.capitalize) {
    modelValue = modelValue.charAt(0).toUpperCase() + modelValue.slice(1);
    }
    </script>

    可以看到,Vue3.4的新特性,还是非常好用的。

    END

    在本文中,我们深入探讨了在Vue框架中,父子组件之间数据通信的核心机制,并特别关注了 v-model 指令的功能与在Vue版本迭代中的变化。通过对 props emit 机制的解析,我们了解了如何在组件间单向传递数据以及如何通知父组件子组件的数据变化。

    随着Vue3起的变更, v-model 的包装参数的名称更加合理,特别是在Vue3.4及以后的版本中引入的 defineModel 配置方式更加简洁和优雅。

    希望文章对你有用嗷。