前言
最近在弄数据库设计的项目的时候,注意到了一个有趣的地方。
首先这是个父子组件的的通信交流。在user.vue组件下,引用了form子组件。那子组件form是一个form表单输入控件,父组件(user.vue)通过props传递formitems数组给子组件(form.vue)循环生成文本输入框。那么当我在子组件下输入了信息,需要把信息传递给父组件外。
一开始,我用最正常的方式也就是props单向数据流传递引用。formdata就是引用值传递。
<template>
<div class="user">
<Form
:formItems="formItems"
:formdata="formdata"
:labelWidth="labelWidth"
/>
</div>
</template>
<script>
import Form from "@/base/form/src/form.vue";
import { reactive } from "vue";
export default {
components: { HyForm },
setup(props) {
const formItems = [
{
field: "id",
type: "input",
label: "id",
placeholder: "请输入id",
},
{
field: "name",
type: "input",
label: "用户名",
placeholder: "请输入用户名",
},
{
field: "sex",
type: "input",
label: "性别",
placeholder: "请输入性别",
}
];
const labelWidth = "110px";
const formdata = reactive({
id: "",
name: "",
sex: ""
});
return {
formItems,
labelWidth,
formdata,
};
},
};
</script>
子组件接收并且循环v-model绑定值
<template>
<div class="HyForm">
<el-row>
<template v-for="item in formItems" :key="item.label">
<el-col :span="colayout">
<template v-if="item.type === 'input'">
<el-form>
<el-form-item :label="item.label">
<el-input
v-model="formdata[`id`]"
:placeholder="item.placeholder"
/>
</el-form-item>
</el-form>
</template>
</el-col>
</template>
</el-row>
</div>
</template>
<script>
export default{
props:{
formdata: {
required: true,
type: Object,
},
}
}
</script>
但是问题来了
每个el-input在遍历的时候会自动绑定formdata[${item.field}],这样进行父组件的formdata进行双向绑定。但是,vue的eslint就给我报错了。
error的报错信息大致是 eslint并不支持formdata的prop修改。也就是对我子组件引用父组件的数据会做出修改的提前报错。居然不给我修改props的权利?我先百度了一下,通过"vue/no-mutating-props": "off"给关闭了。这是最直接的办法,虽然说vue不接受直接改变props的数据去双向绑定。
网上的例子
父子组件的传递通过props,和emit通信。照网上的办法试一下通过计算属性,设置get和set方法,也就是不直接修改props而是通过emit触发事件给父组件修改。
父组件改变用v-model传递引用
<Form
:formItems="formItems"
v-model="formdata"
:labelWidth="labelWidth"
/>
子组件用modelVal接收props
modelValue: {
type: Object,
required: true,
},
scripts:
setup(props, { emit }) {
const formdata = computed({
get: () => props.modelValue,
set: (newVal) => {
console.log(newVal);
emit[("update:modelValue", newVal)];
},
});
return {
formdata,
};
},
打开vue-devtools,输入文本框看父组件formdata
父组件成功被修改了formdata数据,而且eslint也不报错prop。原来以为这种方式已经成功了,但是我发现set方法中的log没反应?如果说是vue3的,我用vue2写法尝试
computed: {
formdata: {
get: function () {
console.log(this.modelValue);
return this.modelValue;
},
set: function (newval) {
console.log(newval);
this.$emit("update:modelValue", newval);
},
},
},
一样的set方法没有触发,我尝试用watch监听这个formdata,但是监听不到computed里的。难道是用reactive定义的props传递问题?改用ref定义formdata,依然没反应的set方法。
说明根本监听不到这个set变化
这说明了computed实现v-model双向绑定只有get方法,也就是还是改变了props的引用实现,而不是通过计算变化触发emit事件让父组件改变。
既然计算属性行不通,那么尝试用v-model组件绑定并且emit触发。
简单演示v-model的原理
child.vue
<Form v-model="formdata" />
//==等价于
<Form :formdata="formdata" @update:modelValue="formdata = "$event.target" />
user.vue
//用v-model传值给form.vue
<Form :formItems="formItems" v-model="formdata" :labelWidth="labelWidth" />
form.vue
import { watch, ref } from "vue";
props:{//定义formdata的接收
modelValue: {
type: Object,
required: true,
},
}
setup(props, { emit }) {
//用ref定义响应式对象,并且用扩展运算符展开props引用,return出去
//用watch去监听formdata变化,newval值通过emit传送
const formdata = ref({ ...props.modelValue });
watch(
formdata,
(newVal) => {
emit("update:modelValue", newVal);
},
{ deep: true } //vue3的监听器的深度监听的配置属性
);
return {
formdata,
};
},
//来源coderwhy大神的思路
先验证控制台打印是否实现双向绑定结果,form组件改变emit触发修改userformdata
为了确定是通过emit('update:modelValue')触发修改,监听update-modelValue事件
<Form
:formItems="formItems"
v-model="formdata"
@update:modelValue="handlemodelValue"
:labelWidth="labelWidth"
/>
<script>
const handlemodelValue = (item) => {
console.log(item);
};
</script>
打开控制台
结果说明确实可以实现emit双向绑定功能。
总结
1.通过props传递引用单向数据流,必要时可关闭eslint报错。
2.通过v-model传值,子组件定义扩展运算符响应式对象返回页面,watch监听emit('update:modelValue')
3.通过vuex全局使用获取
失败:
1.无法确定props传值,computed设置计算属性set方法失效的原因。
参考资料
[1] vue.js
cn.vuejs.org/v2/guide/cu…
来源:
[1] 博客 blog
mjcelaine.top