Vue3通过v-model封装第三方组件

4,609 阅读1分钟

Vue3通过v-model封装第三方组件

实际场景: 页面上点击一个按钮使用第三方库弹出一个模态框(如element-plus<el-dialog></el-dialog>),模态框中有复杂的业务逻辑,于是你想把模态框封装成一个新的组件。

v-model

Vue3.x 中,自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件。

<ChildComponent v-model="pageTitle" />

<!-- 是以下的简写: -->

<ChildComponent :modelValue="pageTitle" @update:modelValue="pageTitle = $event" />

下面是带参数的:

<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />

<!-- 是以下的简写: -->

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" :content="pageContent" @update:content="pageContent = $event" />

子组件:

<template>
	<input type="text" :value="modelValue" @input="handleInput" />
</template>
<script>
export default defineComponent({
	props: {
		modelValue: String,
	},
	emits: ["update:modelValue"],
	methods: {
		handleInput(xxx) {
			this.$emit("update:modelValue", xxx.target.value);
		},
	},
});
</script>

父组件不是默认的,带参数的子组件:

<template>
	<input type="text" :value="pageTitle" @input="handleInput" />
</template>
<script>
export default defineComponent({
	props: {
		pageTitle: String,
	},
	emits: ["update:pageTitle"],
	methods: {
		handleInput(xxx) {
			this.$emit("update:pageTitle", xxx.target.value);
		},
	},
});
</script>

使用第三方组件

父组件:

<template>
<Test :modelValue="testVal" @update:modelValue="testVal = $event">
<!-- 简写 -->
<Test v-model="testVal" />
</template>

子组件: 问题版

<template>
	<!-- 会有问题: -->
	<!-- modelValue是往下传给el-input组件的prop值,handler是el-input组件modelValue值更新触发的钩子 -->
	<el-input :modelValue="modelValue" @update:modelValue="handler" @input="handleElInput"></el-input>
	<!-- 简写: -->
	<el-input v-model="modelValue" @input="handleElInput"></el-input>
</template>
<script>
import { defineComponent } from "vue";

export default defineComponent({
	props: ["modelValue"],
	emits: ["update:modelValue"],
	methods: {
		handler(a) {
			// 此处的a就是el-input组件emit('update:modelValue',b)返回的参数b,它返回的是input输入的value值
			this.modelValue = a; //此处会报错,prop是readonly
		},
		handleElInput(a) {
			// 此处的a就是el-input组件emit('input',b)返回的参数b,它返回的是input输入的value值
			this.$emit("update:modelValue", a); // 把a返回给父组件,input时通过触发update:modelValue更新输入的内容
		},
	},
});
</script>

正确做法:

<template>
	<!-- 正确做法:通过一个计算属性 myValue 中转一下 -->
	<el-input :modelValue="myValue" @update:modelValue="handler" @input="handleElInput"> </el-input>
	<!-- 简写 -->
	<el-input v-model="myValue" @input="handleElInput"></el-input>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
	props: ["modelValue"],
	emits: ["update:modelValue", "myInput"],
	methods: {
		handler(a) {
			// 此处的a就是el-input组件emit('update:modelValue',b)返回的参数b,它返回的是input输入的value值
			// 此处我们把el-input返回的a赋值给myValue
			this.myValue = a;
		},
		handleElInput(a) {
			// 此处的a就是el-input组件emit('input',b)返回的参数b,它返回的是input输入的value值
			this.$emit("myInput", a); //触发父组件myInput事件,把a返回给父组件。(所以父组件可以写 @myInput = "xxx")
		},
	},
	computed: {
		myValue: {
			get() {
				return this.modelValue;
			},
			set(v) {
				// 此处的v就是 handler 函数中设置的值a
				this.$emit("update:modelValue", v);
			},
		},
	},
});
</script>