Vue组件通信之v-model

文章目录

总结

  • v-model 的本质是 v-bind 加上 @input ,实现数据双向绑定
  • 父组件和子组件之间通信:
    • 通过 props 自定义属性,实现从父到子的数据传送
    • 通过 emit 自定义事件,实现从子到父的数据传送
    • 二者相结合,实现父子组件之间的数据双向绑定
  • 在父组件里,可以简化 为v-model 操作,前提是子组件emit的事件必须是 update:xxx
  • v-model 的默认参数(即绑定的属性名)是 modelValue
    • 对于原生HTML元素, v-model 无需指定属性名,因为Vue知道取哪个属性名
  • 同一个子组件里可通过多个v-model 绑定多个属性,只需用参数区分开

环境

  • Ubuntu 24.04
  • Chrome Version 146.0.7680.164 (Official Build) (64-bit)
  • VSCode 1.109.5
  • npm 11.6.2
  • Vue 3.5.32

准备

创建一个Vue项目。创建过程略,具体可参见 https://blog.csdn.net/duke_ding2/article/details/159510007

关于props,参见 https://blog.csdn.net/duke_ding2/article/details/159696940

关于emit,参见 https://blog.csdn.net/duke_ding2/article/details/159779784

v-model

基础

修改 App.vue 如下:

html 复制代码
<template>
    <div>
        <input v-model="searchText" />
        <div>{{ searchText }}</div>
        <button @click="searchText = Math.random()">随机数</button>
    </div>
</template>

<script>
export default {
    data() {
        return {
            searchText: ''
        }
    }
}
</script>

效果如下:

  • 点击按钮时,生成一个随机数:
  • 用户可以修改输入框的数据:

本例中,通过 v-model ,实现了数据(变量 searchText )与显示的"双向绑定":

  • 当数据的值变化时(比如点击按钮,生成随机数),输入框里的数据会同步变化
  • 当输入框里的数据被修改时,数据的数值会同步变化

注意这行代码:

html 复制代码
<input v-model="searchText" />

模板编译器会对 v-model 进行更冗长的等价展开。因此上面的代码其实等价于下面这段:

html 复制代码
<input
  :value="searchText"
  @input="searchText = $event.target.value"
/>

其中:

  • :value 表示从 searchText 变量到输入框显示的绑定
  • @input 表示在编辑输入框时,把数据修改同步到 searchText 变量

组件

在使用子组件时,要想把"父组件里的数据"和"子组件里的显示"做双向绑定,同理也需要 :value@input ,但不同的是:

  • :value :由于其来源是父组件,所以需要通过 props 设置自定义属性
  • @input :由于其目的是父组件,所以需要通过 emit 把事件发送到父组件

创建 MyComponent.vue 文件如下:

html 复制代码
<template>
    <input :value="searchText" @input="$emit('update:searchText', $event.target.value)"/>
</template>

<script>
export default {
    props: ['searchText'],
    emits: ['update:searchText'],
}
</script>

可见,在子组件中,通过 props 接受父组件的数据,通过 emit 向父组件发送"数据修改"事件。

注意:本例中,emit的事件名是 update:searchText ,其实在本例中并不强制如此。

修改 App.vue 如下:

html 复制代码
<template>
    <div>
        <MyComponent :searchText="searchText" @update:searchText="newValue => searchText = newValue"/>
        <div>{{ searchText }}</div>
        <button @click="searchText = Math.random()">随机数</button>
    </div>
</template>

<script>
    import MyComponent from './MyComponent.vue';
    export default {
        components: {
            MyComponent
        },

        data() {
            return {
                searchText: ''
            }
        }
    }
</script>

可见,在父组件中,通过自定义属性 :searchText 向子组件传送数据,通过自定义事件 @update:searchText 接收子组件的"数据修改"事件,并同步修改变量 searchText

这样,就实现了父组件数据和子组件显示之间的双向绑定。

注意:本例中,自定义事件名是 update:searchText ,其实换成的其它如 updateSearchText 也没有问题。

v-model

之所以使用 update:searchText 这种看起来有点"怪异"的写法,是因为在Vue里,使用这种方法,则在父组件可以简化为 v-model 操作。

找到 App.vue 的如下代码:

html 复制代码
<MyComponent :searchText="searchText" @update:searchText="newValue => searchText = newValue"/>

将其修改为:

html 复制代码
<MyComponent v-model:searchText="searchText"/>

同样也能达到双向绑定效果。

只需记住 update:xxx 是固定的写法。这样父组件就可以通过 v-model 来简化。

参数与默认绑定属性名

上面的例子里, v-model 有一个参数 searchText ,该参数代表绑定的属性名。

注意:在本例中,该参数不能省略:

html 复制代码
<MyComponent v-model="searchText"/>

这是不行的,因为Vue需要知道绑定的属性名是什么。如果不显式指定,则缺省绑定的属性名是 modelValue

也就是说,如果子组件只接收一个自定义属性,则为了简化,可以将其命名为 modelValue ,此时父组件的 v-model 可以省略参数。

注意:文本开头的例子里,并没有指定属性名:

html 复制代码
<input v-model="searchText" />

这是因为对于原生的HTML元素,Vue知道应该绑定哪个属性名。比如,对于 input ,默认绑定的属性名是 value

多个v-model绑定

既然通过参数可以区分不同的属性名,我们就可以在一个子组件里通过 v-model 绑定多个属性。

修改 MyComponent.vue 如下:

html 复制代码
<template>
    <div>
        <input :value="searchText" @input="$emit('update:searchText', $event.target.value)"/>
        <br />
        <input :value="searchTitle" @input="$emit('update:searchTitle', $event.target.value)"/>
    </div>
</template>

<script>
export default {
    props: ['searchText', 'searchTitle'],
    emits: ['update:searchText', 'update:searchTitle'],
}
</script>

修改 App.vue 如下:

html 复制代码
<template>
    <div>
        <MyComponent v-model:searchText="searchText" v-model:searchTitle="searchTitle"/>
        <div>{{ searchText }}</div>
        <div>{{ searchTitle }}</div>
        <button @click="searchText = Math.random()">随机数</button>
        <button @click="searchTitle = Math.random()">随机数</button>
    </div>
</template>

<script>
    import MyComponent from './MyComponent.vue';
    export default {
        components: {
            MyComponent
        },

        data() {
            return {
                searchText: '',
                searchTitle: ''
            }
        }
    }
</script>

效果如下:

参考

  • https://cn.vuejs.org/guide/components/v-model.html
相关推荐
像素之间2 小时前
为什么运行时要加set NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve
前端·javascript·vue.js
M ? A2 小时前
Vue转React实战:defineProps精准迁移实战
前端·javascript·vue.js·经验分享·react.js·开源·vureact
西陵2 小时前
别再写 Prompt 了Spec Mode 才是下一代 AI 编程范式
前端·人工智能·ai编程
如意猴2 小时前
【前端】002--怎样制作一个简历界面?
开发语言·前端·javascript
NickJiangDev3 小时前
Elpis Schema 动态组件与表单:配置驱动的完整 CRUD 闭环
前端
kerli3 小时前
Compose 组件:Box 核心参数及其 Bias 算法
android·前端
luckyCover3 小时前
TypeScript学习系列(二):高级类型篇
前端·typescript
NickJiangDev3 小时前
Elpis NPM 发布:把框架从业务中剥离出来
前端
comerzhang6553 小时前
手撕 V8:我是如何用 2.67ms 的心跳活捉 700ms 冻结幽灵的
javascript