Vue3自定义组件里数据双向绑定的实现

58 阅读2分钟

一、组合式API里的写法

1、defineModel

defineModel()返回的值是一个ref,它可以像其他ref一样被访问以及修改,不过它能起到在父组件和当前变量之间的双向绑定的作用。

  • 它的.value和父组件的v-model的值一起更新
  • 当它的子组件变更了,会触发父组件绑定的值一起更新

因为defineModel声明了一个prop,你可以通过给它传递选项,来声明底层prop的选项

const model = defineModel('modelValue', {
requiew: true, // 必填
default: 0, // 默认值
type: Number // 类型
})

注意:该写法需Vue3.2.0及以上版本才支持

1个数据的双向绑定

// 父组件
 
<template>
 
<p>{{ data }}</p>
<button @click="data = 'xxx2'">修改子组件数据</button>
 
 
<Subcomponent v-model="data" />
 
</template>
 
<script lang="ts" setup>
 
import { ref } from 'vue'
import Subcomponent from './components/Subcomponent.vue'
 
const data = ref('xxx1')
 
 
</script>
// 子组件
<template>
 
<h1>{{ modelValue }}</h1>
<button @click="updateData('xxx2')">修改父组件的数据</button>
 
</template>
 
<script lang="ts" setup>
 
import { defineModel } from 'vue'
 
const modelValue = defineModel()
 
const updateData = (e: string) => {
    modelValue.value = e
}
 
</script>

多个数据的双向绑定

// 父组件
 
<template>
 
<p>{{ data1 }}</p>
<p>{{ data2 }}</p>
<button @click="data1 = 'xxx2'">修改子组件的数据1</button>
<button @click="data2 = 'xxx2'">修改子组件的数据2</button>
 
 
<Subcomponent v-model:data1="data1" v-model:data2="data2" />
 
</template>
 
<script lang="ts" setup>
 
import { ref } from 'vue'
import Subcomponent from './components/Subcomponent.vue'
 
const data1 = ref('data1')
const data2 = ref('data2')
 
 
</script>
// 子组件
<template>
 
<h1>{{ data1 }}</h1>
<h1>{{ data2 }}</h1>
<button @click="updateData1('xxx1')">修改父组件的数据1</button>
<button @click="updateData2('xxx2')">修改父组件的数据2</button>
 
</template>
 
<script lang="ts" setup>
 
import { defineModel } from 'vue'
 
const data1 = defineModel('data1')
const data2 = defineModel('data2')
 
const updateData1 = (e: string) => {
    data1.value = e
}
 
const updateData2 = (e: string) => {
    data2.value = e
}
 
</script>

v-model

1个数据的双向绑定

// 父组件
 
<template>
 
<p>{{ data }}</p>
<button @click="data = 'xxx2'">修改子组件数据</button>
 
 
<Subcomponent v-model="data" />
 
</template>
 
<script lang="ts" setup>
 
import { ref } from 'vue'
import Subcomponent from './components/Subcomponent.vue'
 
const data = ref('xxx1')
 
 
</script>
// 子组件
<template>
 
<h1>{{ modelValue }}</h1>
<button @click="updateData('xxx2')">修改父组件的数据</button>
 
</template>
 
<script lang="ts" setup>
 
const props = defineProps<{ modelValue: string }>()
const emit = defineEmits<(e: "update:modelValue", value: string) => void>()
 
const updateData = (e: string) => {
    emit('update:modelValue', e)
}
 
</script>

多个数据的双向绑定

// 父组件
 
<template>
 
<p>{{ data1 }}</p>
<p>{{ data2 }}</p>
<button @click="data1 = 'xxx2'">修改子组件的数据1</button>
<button @click="data2 = 'xxx2'">修改子组件的数据2</button>
 
 
<Subcomponent v-model:data1="data1" v-model:data2="data2" />
 
</template>
 
<script lang="ts" setup>
 
import { ref } from 'vue'
import Subcomponent from './components/Subcomponent.vue'
 
const data1 = ref('data1')
const data2 = ref('data2')
 
 
</script>
// 子组件
<template>
 
<h1>{{ modelValue }}</h1>
<button @click="updateData1('xxx2')">修改父组件的数据1</button>
<button @click="updateData2('xxx2')">修改父组件的数据2</button>
 
</template>
 
<script lang="ts" setup>
 
const props = defineProps<{ 
data1: string, 
data2: string 
}>()
 
const emit = defineEmits<{
(e: "update:data1", value: string): void,
(e: "update:data2", value: string): void,
}>()
 
const updateData1 = (e: string) => {
    emit('update:data1', e)
}
 
const updateData2 = (e: string) => {
    emit('update:data2', e)
}
</script>

常规写法

// 子组件
 
<template>
    <p>{{ p_data }}</p>
    <button @click="updateData('xxx')">子组件按钮</button>
</template>
 
<script lang="ts" setup>
 
const props = defineProps<{ p_data: string }>()
 
const emit = defineEmits<(e: "update", value: string) => void> ()
 
function updateData(e: string) {
    emit('update', '修改父组件data')
}
 
</script>
// 父组件
 
<template>
 
<p>{{ data }}</p>
<button @click="data = 'xxx2'">修改子组件数据</button>
 
<Subcomponent :p_data="data" @update="updateData"/>
 
</template>
 
<script lang="ts" setup>
 
import { ref } from 'vue'
import Subcomponent from './components/Subcomponent.vue'
 
const data = ref('xxx1')
 
function updateData(e: string) {
    data.value = e
}
 
</script>

选项式API里的写法

// 子组件
<template>
<input :value="modelValue" @input="handleInput" />
</template>
 
<script>
 
export default {
    props: {
        modelValue: String // 定义v-model绑定的属性名
    },
 
    emits: ['update:modelValue'] // 声明可以发出的自定义事件
}
 
</script>
// 父组件
<template>
<CustomInpput v-model="inputValue" />
</template>
 
<script>
import CustomInput from './CustomInput.vue'
 
export default {
    components: {
        CustomInput
    },
    
    data() {
        return {
            inputValue: ''
        }
    }
}
</script>

案例 —— 自定义勾选按钮

<template>
  <div class="component-container">
		<i class="check-btn" v-if="!isChecked" @click="handleCheck(true)"></i>
		<i class="check-btn active" v-if="isChecked" @click="handleCheck(false)"></i>
	</div>
</template>
 
<script lang="ts" setup>
 
import { ref, defineModel } from 'vue'
 
const emits = defineEmits(['change'])
 
const isChecked = defineModel('isChecked', { default: false })
 
 
function handleCheck(e: boolean) {
	isChecked.value = e
 
	emits('change',  isChecked.value)
}
 
</script>
 
<style lang="scss" scope>
.component-container {
	.check-btn {
		display: block;
		border: 1px solid #d4d4d9;
		width: 15px;
		height: 15px;
		cursor: pointer;
		&.active {
			background: #0055FF;
		}
	}
}
</style>
<CheckBtn @change="(...args) => managementFieldCheckAll(...args, it.value)"/>