关于这点其实没有特别多需要讲的点。但是最近写组件的时候感觉v-model如果使用得好其实是可以减少很多的问题的,虽然对高手们来说这些使用方式是很常见的,但是对刚刚接触vue的人,如果能知道v-model在组件中的正确使用方式,其实是可以大幅度提高自己的组件质量的。

首先我们要明确的是,我们所写的组件是有两种类型的,一种是共用的组件,比如共用的
common-header
common-button
这个样子的组件
而另一些组件的使用其实是为了给页面进行一次划分。比如
这里写图片描述
在网易云这个页面,其实可以划分成
热门推荐
个性化推荐
两个组件,当然从这里来看,他们的形式是类似的,应该是可以写成一个组件的。但是这里只是做一个说明。
而这个时候,我们传入到组件中的数据,其实是希望进行双向绑定的。如果在里面的内容是可以修改项的时候。
这种场景比较常见的是在一些注册页面,需要提供的信息很多,所以需要进行模块划分便于维护。而这个时候我们就可以使用组件的
v-model

当组件的内容变化的时候,能够让父组件中的内容也一并变化。实现方式是检测watch,在变化的时候使用

this.$emit('input',value)

就好了
这里要注意的是
v-model其实也是一个props而他在组件中的默认值是value
也就是说,如果你要使用v-model那么你就可以要在子组件中声明

props: {
  value: {
    type: Boolean,
    required: true,
  },

当然你不声明也没有问题,只作为一个接受装置,但是这个样子就失去了意义了。
随后我们要讨论的是,如果我们的组件还有一个v-model怎么办
这句话是什么意思呢?

  <div v-transfer-dom class="common-top-picker">
    <popup
      v-model="show"
      :position="'top'"
      class="pop"
      @on-hide="hidePicker">
      <div class="title float-box border-bottom-line">
        <i class="float-right iconfont icon-fork" @click="hidePicker"/>
        <div class="float-left">{{ title }}</div>
      </div>

在这里我们使用了一个vux的pop组件,他也是需要v-model来进行控制的。我们对这个的封装是因为我们想要一个更加合理的pop,这样比如头部带有两个button的top这样的代码就可以减少编写了。
这里我们可能想的是给
pop的v-model也绑定value,但是很遗憾,这个时候会遇到一个问题。那就是因为pop有一个点击遮罩关闭的功能会自动把value设置为false,在我们单独使用pop的时候这个样子是没有问题的,但是如果我们对pop进行了再一次的封装。那么因为vue不推荐改变props的原因,所以会出现报错。
这个时候我们可以使用计算属性来解决

  computed: {
    show: {
      get () {
        return this.value
      },
      set (v) {
        this.$emit('input', v)
      },
    },
  },

pop的v-model为show在show被改变的时候向外触发事件改变value

这样我们的代码就减少了很多的东西,附上两版代码进行对比
不适用v-model的版本1

<template>
  <div v-transfer-dom class="common-top-picker">
    <popup
      v-model="show"
      :position="'top'"
      class="pop"
      @on-hide="hidePicker">
      <div class="title float-box border-bottom-line">
        <i class="float-right iconfont icon-fork" @click="hidePicker"/>
        <div class="float-left">{{ title }}</div>
      </div>
      <slot/>
      <common-double-button
        :buttonOption="buttonOption"
        @clickLeft="clickLeft"
        @clickRight="clickRight"/>
    </popup>
  </div>
</template>
<script>
import CommonDoubleButton from '&/common/common-double-button'
import { Popup } from 'vux'
export default {
  name: 'CommonTopPicker',
  components: {
    CommonDoubleButton,
    Popup,
  },
  props: {
    showPicker: {
      type: Boolean,
      required: true,
    },
    title: {
      type: String,
      default: '',
    },
    buttonOption: {
      type: Object,
      default: () => { return { left: '取消', right: '确认' } },
    },
  },
  data () {
    return {
    }
  },
  computed: {
    show: {
      get () {
        return this.showPicker
      },
      set () {
      },
    },
  },

  methods: {
    hidePicker () {
      this.$emit('hidePicker')
    },
    clickLeft () {
      // 为了防止某些可能比较奇怪的设计点击左边进行提交。。。所以还是都把值传出去吧
      this.hidePicker()
      this.$emit('clickLeft')
    },
    clickRight () {
      this.hidePicker()
      this.$emit('clickRight')
    },
  },
}
</script>

使用v-model的版本2

<template>
  <div v-transfer-dom class="common-top-picker">
    <popup
      v-model="show"
      :position="'top'"
      class="pop"
      @on-hide="hidePicker">
      <div class="title float-box border-bottom-line">
        <i class="float-right iconfont icon-fork" @click="hidePicker"/>
        <div class="float-left">{{ title }}</div>
      </div>
      <slot/>
      <common-double-button
        :buttonOption="buttonOption"
        @clickLeft="clickLeft"
        @clickRight="clickRight"/>
    </popup>
  </div>
</template>
<script>
import CommonDoubleButton from '&/common/common-double-button'
import { Popup } from 'vux'
export default {
  name: 'CommonTopPicker',
  components: {
    CommonDoubleButton,
    Popup,
  },
  props: {
    value: {
      type: Boolean,
      required: true,
    },
    title: {
      type: String,
      default: '',
    },
    buttonOption: {
      type: Object,
      default: () => { return { left: '取消', right: '确认' } },
    },
  },
  data () {
    return {
    }
  },
  computed: {
    show: {
      get () {
        return this.value
      },
      set (v) {
        this.$emit('input', v)
      },
    },
  },
  methods: {
    hidePicker () {
      this.$emit('input', false)
    },
    clickLeft () {
      // 为了防止某些可能比较奇怪的设计点击左边进行提交。。。所以还是都把值传出去吧
      this.hidePicker()
      this.$emit('clickLeft')
    },
    clickRight () {
      this.hidePicker()
      this.$emit('clickRight')
    },
  },
}
</script>

而在使用方法上
版本1

    <common-top-picker
      :showPicker="addBox"
      :title="'新建任务'"
      @clickRight="submitAdd"
      @clickLeft="hideAddBox"
      @hidePicker="hideAddBox">
      <task-add-box v-model="addTaskData"/>

版本2

    <common-top-picker
      v-model="addBox"
      :title="'新建任务'"
      @clickRight="submitAdd"
      @clickLeft="hideAddBox">
      <task-add-box v-model="addTaskData"/>
    </common-top-picker>

其实如果只是进行关闭得话连clickLeft的hide都可以不需要了。
使用更加的简单了。

其实这个博客讲得东西都是比较简单的。不过使用方法确实是当时自己没有注意到的点。
其实最近在写很多的业务代码,很多时候都要进行组件的封装,而组件的封装,现在越来越觉得是一个技术活了,很多时候因为时间紧,所以就干脆赋值粘贴了,或者封了一个不怎么好用的,也就勉强着用了,感觉这样不好,虽然在写业务、但是其实怎么对业务中的共有进行提取,也是一个挺需要学需要练习的东西,需要好的技术,才能做得更好

Logo

前往低代码交流专区

更多推荐