手动实现一个v-model的不同做法(含封装组件用法)

RadiomM

由于疫情的原因啊,我猜大多数人都跟我一样,在家里度过,偶尔学习学习。这不,我遇到了一个如何手动一个v-model的问题,由此展开了学习。
在开始之前我们先需要知道一些知识,v-model是一个语法糖,如下:
<input type="text" v-model="name">
实则是等于下面的:
<input type="text" :value="name" @input="name=$event.target.value">
也就是说,v-model就是绑定了一个名为value的props和一个事件input

        注意:所以在子组件中可以通过props中定义value接受值,然后通过$emit触发input事件传新值并修改。 

知道了这个知识之后啊,那么我们就开始实现一个自定义的v-model啦~~

一、最简单的实现方法

首先是父组件的代码:

<template>
  <div class="">
      <p>{{name}}</p>
      <son v-model="name"></son>
      <!-- 等同于下面 -->
      <!-- <son type="text" :value="name" @input="name=子组件传回来的值"> -->
</div>
</template>

<script type="text/javascript">
import Son from './son'
export default {
  name: "",
  data() {
    return {
        name:'ydw',
    }
  },
  components: {
      Son
  }
}
</script>

然后是子组件的代码:

<template>
  <div class>
      <input type="text" :value="value" @input="$emit('input',$event.target.value)">
  </div>
</template>

<script type="text/javascript">
export default {
  name: "",
  props:{
      value:{
          type: String,
          defalut () 
              return ''
          }
      }
  },
  data() {
    return {

    }
  },
  components: {
  }
}
</script>

父组件中我加了一个注释的代码行,其实是帮助我自己理解的,不知道有没有小伙伴跟我一样,第一次看的时候压根不知道它在做什么的?哭唧唧。

二、使用watch和$emit实现

跟上面的一个有些类似,只不过上面是用本身input事件的作用,通过input事件的频发触发传递值给父组件,这里只是将这个过程换成了通过watch监听值得变化,然后通过input事件触发函数事件,函数事件里提交变化的值给父组件。好了,我只需要修改子组件,如下:

<template>
  <div class>
    <input type="text" :value="sonVal" @input="handleModel" />
  </div>
</template>

<script type="text/javascript">
export default {
  name: "",
  props: {
    value: {
      type: String,
      defalut() {
        return "";
      }
    }
  },
  data() {
    return {
      sonVal:this.value
    };
  },
  watch: {
    value(newval) {
      this.sonVal = newval
    }
  },
  methods:{
    handleModel(e){
        this.sonVal = e.target.value
        this.$emit('input',this.sonVal)
    }
  }
};
</script>

通过watch监听props值得变化,赋值给子组件本身定义的sonVal,绑定在input里。然后利用input事件触发函数,提交到父组件。

三、使用最新的语法,model属性。

本来我是不知道有这个属性的,就是在网上搜索关于v-model实现方法的时候,就不知不觉的查到这个属性,查看官方文档如下:
2020-03-08_192856.png

也就是说这个属性是为了回避props定义的value这个名字的。

下面看看它的用法,我们先看子组件怎么修改:

<template>
  <div class>
    <input type="text" :value="val" @input="$emit('change',$event.target.value)" />
  </div>
</template>

<script type="text/javascript">
export default {
  name: "",
  model:{
    prop:'val',
    event:'change'
  },
  props: {
    val: {
      type: String,
      defalut() {
        return "";
      }
    }
  },
  data() {
    return {
    };
  }
};
</script>

model属性中prop是重新命名传值的名字,而event则是重新命名事件的名字,但是同样的,props中的传值名字必须与model属性中的prop的名字一样。其实只要对比一下第一种方法就知道,vue多出一个model属性是为了value这个名字可以使用。

同样的,这里也贴出使用新属性后,在使用wacth是什么样的,也是只是修改了son组件

<template>
  <div class>
    <input type="text" :value="sonVal" @input="handleModel" />
  </div>
</template>

<script type="text/javascript">
export default {
  name: "",
  model:{
    prop:'val',
    event:'change'
  },
  props: {
    val: {
      type: String,
      defalut() {
        return "";
      }
    }
  },
  data() {
    return {
      sonVal:this.val
    };
  },
  watch: {
    value(newval) {
      this.sonVal = newval
    }
  },
  methods:{
    handleModel(e){
        this.sonVal = e.target.value
        this.$emit('change',this.sonVal)
    }
  }
};
</script>

其实也没多大的修改,只是想贴出来而已。那么手写一个v-model我就写完了,觉得有用的同志给我点个赞赞赞赞赞赞呗。

分割线

距离文章发布已经有半年多的时间了,最近用vue2.0封装elementUI,在封装form表单的时候恰巧用上了这个吃灰许久的东西记录一下。

首先讲我简单的封装代码上来:

<template>
  <div class="form-container">
    <el-form
      :model="filterObject"
      label-position="left"
      :label-width="labelWidth"
    >
      <el-row :gutter="10">
        <el-col
          v-for="item in formData"
          :key="item.prop"
          :span="item.span || 8"
        >
          <el-form-item :label="item.label">
            <el-input
              v-if="item.type === 'text'"
              v-model="filterObject[item.prop]"
              clearable
              :disabled="item.disabled || false"
              :placeholder="item.placeholder"
            ></el-input>

            <el-select
              v-if="item.type === 'select'"
              v-model="filterObject[item.prop]"
              clearable
              :placeholder="item.placeholder"
              @change="handleChange"
            >
              <el-option
                v-for="sub in item.arr"
                :key="sub.value"
                :label="sub.label"
                :value="sub.value"
              ></el-option>
            </el-select>
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
  </div>
</template>

<script>
export default {
  model: {
    prop: 'filterObject',
    event: 'change',
  },
  props: {
    filterObject: {
      type: Object,
      default() {
        return {}
      },
    },
    labelWidth: {
      type: String,
      default: '80px',
    },
    formData: {
      type: Array,
      default() {
        return []
      },
    },
  },
  data() {
    return {}
  },
  methods: {
    handleChange() {
      console.log(this.filterObject)
    },
  },
}
</script>
复制代码

其中的filterObject就是今天的主角了,我的想法是在父组件直接传递一个对象进来,在进行相关操作的时候,父组件直接可以在外面用到这些值,而不是通过子组件暴露方法获取这些值。(ps:在封装search组件的时候可以这么做,向外暴露search,reset方法可以暴露子组件里面的值。其实封装search跟这个form有点相似,都是用elementUI的form表单来封装的。)

接下来是父组件的代码:

<template>
  <div class="home">
    <HHform
      :formData="searchOptions"
      labelWidth="120px"
      v-model="filterObject"
    ></HHform>
  </div>
</template>

<script>
import HHform from '@/components/HH-form'
export default {
  name: 'Home',
  components: {
    HHform,
  },
  data() {
    return {
      searchOptions: [
        {
          type: 'select',
          prop: 'status',
          label: '状态:',
          span: 5,
          placeholder: '请选择',
          arr: [
            {
              value: '',
              label: '全部招生顾问',
            },
            {
              value: 1,
              label: '正常招顾',
            },
            {
              value: 2,
              label: '冻结招顾',
            },
          ],
        },
        {
          type: 'select',
          prop: 'region',
          label: '地区:',
          span: 5,
          placeholder: '请选择',
          arr: [],
        },
        {
          type: 'text',
          prop: 'userName',
          span: 8,
          label: '招生顾问名称:',
          placeholder: '选择或输入搜索',
        },
        {
          type: 'text',
          prop: 'phone',
          label: '手机号:',
          span: 5,
          placeholder: '选择或输入搜索',
        },
      ],
      filterObject: {},
    }
  },
  methods: {},
  },
}
</script>
复制代码

父组件的代码比较简单,直接通过v-model传值过去就行了。下面是演示效果。

在这里插入图片描述
目前封装form组件的时候是使用这种方法,父组件直接传值的话,不需要子组件提供方法返回值。在封装search组件的时候则是使用子组件暴露方法的方式拿到值的,为什么这么做呢?因为在进行搜索的时候一定会暴露search跟reset方法,这时候带出去就行了。时隔多个月终于是用上的东西,特地记录下来,希望可以帮助正在封装组件的你。喜欢的可以点个赞或者收藏呗。

阅读 5.1k

汪汪汪程序员

23 声望
2 粉丝
0 条评论

汪汪汪程序员

23 声望
2 粉丝
文章目录
宣传栏