Vue | 自定义组件双向绑定基础用法
vue 中,由于单向数据流,常规的父子组件属性更新,需要 在父组件绑定相应属性,再绑定相应事件,事件里去做更新的操作,利用语法糖 可以减少绑定事件的操作。
这里就简单的梳理一下vue提供的双向更新的语法糖用法(水一篇,为了节省篇幅只给了关键部分的代码)。
vue2
v-model
基础使用
<!-- 父组件 -->
<template>
<div class="vmodel-test">
V-model 测试
<hr />
v-model 默认基础类型值 | <span>{{ val }}</span>
<inner v-model="val"></inner>
</div>
</template>
export default {
name: "VmodelTest",
components: { inner },
data() {
return {
val: 11111,
}
}
}
<!-- 子组件 -->
<template>
<div class="inner">
<button @click="onUPdate">更改</button>
</div>
</template>
<script>
export default {
name: "Inner",
props: {
value: {
type: [String, Number],
default: "",
},
},
data() {
return {};
},
methods: {
onUPdate() {
// 更新
this.$emit("input", new Date().toLocaleString());
},
},
};
</script>
简单总结就是, 对于父组件 v-model 绑定的值,子组件 需要 定义一个 value 的props,然后更新时使用 input 事件 去更新值,适用于基础类型和对象类型,⚠️需要注意的是这种更新是全量更新(覆盖式)。
比如我 v-model 接受的是一个对象,我只想更新对象的 a 属性
// 更新
this.$emit("input", {
...this.value,
a: new Date().toLocaleString(),
});
自定义更新事件名
model: 允许一个自定义组件在使用
v-model
时定制 prop 和 event。默认情况下,一个组件上的v-model
会把value
用作 prop 且把input
用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用value
prop 来达到不同的目的。使用model
选项可以回避这些情况产生的冲突。API — Vue.js
input
是默认事件,可以自定义,需要 在子组件 写一个 model(2.2新增) 属性去更改
model: {
prop: "value", // 指定是更新value
event: "custom-update-value", // 一般以 update:value 命名
},
props: {
value: {
type: [Array, Object],
default: () => {},
},
},
那么 更新时只需要用新的事件名,⚠️原来默认input事件已经失效
// 更新
this.$emit("custom-update-value", {
...this.value,
a: new Date().toLocaleString(),
});
sync 修饰符
sync 修饰符也双向更新的一个方法,更适用于多个属性需要双向更新的场景
首先在父组件中绑定绑定的属性后面,跟上sync
修饰符
sync 修饰符 | <span>{{ modelValue3 }}</span>
<inner3 :model-value.sync="modelValue3"></inner3>
子组件更新时, 事件名 update:
拼上 属性名
//...
props: {
modelValue: {
type: [Array, Object],
default: () => {},
},
},
//...
// 更新
this.$emit("update:modelValue", {
...this.value,
b: [1, new Date().toLocaleString()],
});
也是全量更新, ⚠️注意这种方式不支持 model 方式自定义事件名
一次性绑定多个(v-bind.sync)
支持 v-bind
一次性绑定多个值的方式
<!-- -->
sync 修饰符 对象解构传入(vbind) | <span>{{ modelValue4 }}</span>
<inner4 v-bind.sync="modelValue4"></inner4>
//...(节省篇幅,只给出相应的data)
modelValue4: {
a: { val: 111 },
b: [1, 2]
},
/** 子组件 **/
props: {
a: {
type: [Array, Object],
default: () => {},
},
b: {
type: [Array, Object],
default: () => {},
},
},
//...
// 更新
this.$emit("update:a", {
...this.a,
val: new Date().toLocaleString(),
});
this.$emit("update:b", [1, new Date().toLocaleString()]);
vue3
v-model
父组件传的v-model,子组件用 modelValue 接收
<!-- 父组件 -->
v-model 默认-对象类型值 | <span>{{ modelValue1 }}</span>
<inner1 v-model="modelValue1"></inner1>
子组件 使用 update:modelValue
更新值
<!-- 子组件 -->
<script setup lang='ts'>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const onUPdate = () => {
// 更新
emit("update:modelValue", {
...props.modelValue,
a: new Date().toLocaleString(),
});
}
</script>
均是全量更新
绑定多个值
<!-- 父组件 -->
v-model 绑定多个值 | <span>{{ modelValue2 }} | {{ modelValue21 }}</span>
<inner2
v-model:modelCusValue="modelValue2"
v-model:modelCusValue1="modelValue21"
></inner2>
/** 子组件 **/
<script setup lang='ts'>
const props = defineProps(['modelCusValue', 'modelCusValue1'])
const emit = defineEmits(['update:modelCusValue', 'update:modelCusValue1'])
const onUPdate = () => {
// 更新
emit("update:modelCusValue", {
...props.modelCusValue,
a: new Date().toLocaleString(),
});
emit("update:modelCusValue1", {
...props.modelCusValue1,
a: new Date().toLocaleString(),
});
}
</script>
也是全量更新
defineModel
此宏为 3.4 新增,使用起来比前面的方式稍微简洁一些。装个
vue-eslint-parser
且在vite.config.ts
做如下配置 plugins, 避免编译器报错。export default defineConfig({ plugins: [ vue({ // 开启 definModel script: { defineModel: true } }), ] //... })
<!-- 父组件 -->
defineModel() | <span>{{ modelData }} | {{ modelData1 }} | {{ modelData2 }}</span>
<inner3
v-model="modelData"
v-model:modelData1="modelData1"
v-model:modelData2="modelData2"
></inner3>
//...
const modelData1 = ref({
a: 1,
b: [1, 2],
})
const modelData2 = ref({
a: 1,
b: [1, 2],
})
此方法在子组件使用 直接使用 属性 value
赋值就行了
<script setup lang='ts'>
type modelValueType = {
a: number | string,
b: Array<number>
}
let model = defineModel<modelValueType>({ required: true })
let model1 = defineModel<modelValueType>('modelData1', { required: true })
let model2 = defineModel<modelValueType>('modelData2', {
required: true,
type: Object,
default: { a: 0, b: [2,3] }
})
const onUPdate = () => {
// 更新
model.value = {
...model.value,
b: [2, 1],
a: new Date().toLocaleString()
}
model1.value.a = new Date().toLocaleString();
model2.value.a = new Date().toLocaleString();
// 或者如下方式
// Object.assign(model.value, { a: new Date().toLocaleString() });
}
</script>
这种方式 支持单个属性更新,全量更新只需全部赋值,比较灵活
最后
若行文有误,望评论区交流。