Vue用v-model实现props传值修改

3,682 阅读2分钟

前言

最近在弄数据库设计的项目的时候,注意到了一个有趣的地方。

首先这是个父子组件的的通信交流。在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