Vue3组件数据双向绑定

目录

组件数据的双向绑定

利用组件通信机制手搓

使用defineModel语法

效果:

defineModel()研究

defineModel的底层机制

defineModel参数

ref选项

ref名字


组件数据的双向绑定

Vue3中的组件数据双向绑定是困扰作者许久的问题,今天因为公司需要,系统的学习了一遍,发现很多有趣的东西,为此在这里记录下来,希望可以帮助到同学们。

目前对于组件数据的双向绑定 有两种形式:"利用组件通信机制手搓"(啰嗦,但有助于理解原理 )、"使用defineModel语法"(Vue3.4+更新,方便快捷

利用组件通信机制手搓

  • defineProps:接收父组件的数据(正向更新)
  • defineEmits:向父组件传递数据(反向更新)

下面我们用一个"子组件封装一个输入框,父组件调用并实时显示数据"的例子来说明:

子组件(child.vue)

javascript 复制代码
<script setup>
const props = defineProps({
    label:{
        type: String,
        default: "测试名"
    },
	modelValue:{
		type: String,
		default: "测试值"
	}
})

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

</script>

<template>
	<div>
		<span>{{ props.label }}</span>
		<input type="text" :value="props.modelValue" @input="emits('update:modelValue',$event.target.value)">
	</div>
</template>

<style scoped>
</style>

父组件(App.vue)

javascript 复制代码
<script setup>
import { ref } from 'vue';
import child from './components/child.vue';

const inputValue = ref('');

</script>

<template>
<div class="container">
    <child 
        :modelValue="inputValue"
        @update:modelValue="inputValue = $event"
    />
    <p>{{ inputValue }}</p>
</div>
</template>

<style scoped>
</style>

注意

  • update:modelValue :这是Vue3里约定的更新数据的事件名,格式为"update:prop",在这个代码中,propmodelValue ,因此事件名为"update:modelValue"
  • @input:当输入框数据发生变化时,向父组件传递数据

此时,你可能会发现,父组件要使用:"modelValue "和"update:modelValue "两处属性槽,比较啰嗦。那么此时就可以使用Vue3 提供给我们的一个"v-model "语法糖

html 复制代码
<child 
    v-model="inputValue"
/>

上面的代码就相当于:

html 复制代码
<child 
    :modelValue="inputValue"
    @update:modelValue="inputValue = $event"
/>

值得注意的是:"尽管父组件可以使用v-model省略,但是子组件的update:modelValue事件不可省略"

使用defineModel语法

使用defineModel 语法要求Vue 版本大于3.4

子组件(child.vue)

javascript 复制代码
<script setup>
const model = defineModel()
const props = defineProps({
    label:{
        type: String,
        default: "测试名"
    }
})
</script>

<template>
	<div>
		<span>{{ props.label }}</span>
		<input type="text" v-model="model" />
	</div>
</template>

<style scoped>
</style>

父组件(App.vue)

javascript 复制代码
<script setup>
import { ref } from 'vue';
import child from './components/child.vue';

const inputValue = ref('');

</script>

<template>
<div class="container">
    <child 
        v-model="inputValue"
    />
    <p>{{ inputValue }}</p>
</div>
</template>

<style scoped>
</style>

效果:

可以看到使用defineModel() 可以非常轻松地实现组件间的双向数据绑定

defineModel()研究

defineModel的底层机制

defineModel 返回的值是一个ref

defineModel是一个便利宏,编译器会解析为:

  • 一个名为modelValue 的prop,本地ref的值与其同步
  • 一个名为update:modelValue 的事件,当本地ref的值发生变更时修改

大白话

  • defineModel返回一个ref,当这个ref值变化时,会自动调用defineModel解析的update:modelValue事件
  • defineModel解析的update:modelValue事件会传递当前ref的值

defineModel参数

ref选项

defineModel 可以接收一个"对象 "来配置ref的一些选项

javascript 复制代码
// 该v-model必填
const model = defineModel({required: true})
// 默认值
const model = defineModel({default: 0})

注意

  • 当给model绑定一个"default默认值"时,如果父组件传入的v-model参数有值,那么该默认值会被顶替
  • 如果父组件传入的v-model参数为空,那么该默认值会生效,但父组件与子组件之间不同步,此时父组件值为空,子组件值为默认值

子组件(child.vue)

javascript 复制代码
<script setup>
const model = defineModel({default: "123"})
const props = defineProps({
    label:{
        type: String,
        default: "测试名"
    }
})
</script>
<template>
	<div>
		<span>{{ props.label }}</span>
		<input type="text" v-model="model"/>
	</div>
</template>
<style scoped>
</style>

父组件(parent.vue)

javascript 复制代码
<script setup>
import { ref } from 'vue';
import child from './components/child.vue';

const titleValue = ref();

</script>

<template>
<div class="container">
    <child 
        v-model="titleValue"
    />
    <p>{{ titleValue }}</p>
</div>
</template>

<style scoped>
</style>

效果

当组件初次载入时,此时父组件和子组件之间的数据不同步,当修改子组件数据后,父子组件数据同步

原因

  • 组件载入时,父组件向子组件传参数值,参数值为空,因此子组件默认值生效,子组件参数值为123
  • 子组件默认值生效后,没有向父组件传递参数值,此时父组件参数值仍为空
  • 当子组件参数值修改后,触发底层update:modelValue方法,向父组件传递参数值
  • 父组件接收到参数值,修改本地参数值,实现数据同步

ref名字

defineModel 可以接收一个"字符串"作为返回ref的名字,利用该特性我们可以同时绑定多个v-model

用法:

html 复制代码
// 指定一个title名字
const model = defineModel('title')
// 父组件调用时,指定传给名为title的ref
<child 
    v-model:title="inputValue"
/>

多个v-model绑定:

子组件(child.vue)

javascript 复制代码
<script setup>
const modelTitle = defineModel('title')
const modelDescription = defineModel('description')
const props = defineProps({
    label:{
        type: String,
        default: "测试名"
    }
})
</script>
<template>
	<div>
		<span>{{ props.label }}</span>
		<input type="text" v-model="modelTitle" style="margin-right: 1rem"/>
		<input type="text" v-model="modelDescription" />
	</div>
</template>
<style scoped>
</style>

父组件(parent.vue)

javascript 复制代码
<script setup>
import { ref } from 'vue';
import child from './components/child.vue';

const titleValue = ref('');
const descriptionValue = ref('');

</script>

<template>
<div class="container">
    <child 
        v-model:title="titleValue"
        v-model:description="descriptionValue"
    />
    <p>{{ titleValue }}</p>
    <p>{{ descriptionValue }}</p>
</div>
</template>

<style scoped>
</style>

效果

相关推荐
江拥羡橙4 小时前
Vue和React怎么选?全面比对
前端·vue.js·react.js
千码君20164 小时前
React Native:快速熟悉react 语法和企业级开发
javascript·react native·react.js·vite·hook
楼田莉子5 小时前
Qt开发学习——QtCreator深度介绍/程序运行/开发规范/对象树
开发语言·前端·c++·qt·学习
暮之沧蓝6 小时前
Vue总结
前端·javascript·vue.js
木易 士心6 小时前
Promise深度解析:前端异步编程的核心
前端·javascript
im_AMBER6 小时前
Web 开发 21
前端·学习
又是忙碌的一天6 小时前
前端学习day01
前端·学习·html
Joker Zxc6 小时前
【前端基础】20、CSS属性——transform、translate、transition
前端·css
excel6 小时前
深入解析 Vue 3 源码:computed 的底层实现原理
前端·javascript·vue.js
大前端helloworld7 小时前
前端梳理体系从常问问题去完善-框架篇(react生态)
前端