v-model做为vue中非常出色的语法糖,应该大家都对它用过了不少了,这里不在过多说明了,重点讲解一下v-model绑定的是一个对象在子组件怎么保持单项数据流,实现正确使用v-model

常见写法

v-model绑定Object对象,在项目中见到很多人都是这样写,简单方便。就只是将单个换成Object类型就行了。
image.png
父组件

<template>
  <div class="model_test">
    <h2>父组件:{{ inputValue }}</h2>
    <input-children v-model:value="inputValue"></input-children>
  </div>
</template>

<script>
import inputChildren from './inputChildren.vue'
export default {
  components: {
    inputChildren
  },
  data() {
    return {
      inputValue: {
        name: '',
        region: 'shanghai',
        delivery: false,
        type: ''
      }
    }
  }
}
</script>

<style lang="less" scoped>
.model_test {
  margin-left: 50px;
  display: flex;
  flex-direction: column;
}
</style>

子组件

<template>
  <div style="width: 200px">
    <el-form label-position="top" label-width="80px" :model="value">
      <el-form-item label="名称">
        <el-input v-model="value.name"></el-input>
      </el-form-item>
      <el-form-item label="活动区域">
        <el-select v-model="value.region" placeholder="活动区域">
          <el-option label="上海" value="shanghai"></el-option>
          <el-option label="北京" value="beijing"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="即时配送" prop="delivery">
        <el-switch v-model="value.delivery"></el-switch>
      </el-form-item>
      <el-form-item label="活动形式">
        <el-input v-model="value.type"></el-input>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: Object,
      default: ''
    }
  }
}
</script>

乍一看可以用,好像没什么问题,确实可以用。
破坏了vue单项数据流的原则,在子组件改变了父组件的数据。
每打破一次单向数据流,你的代码离屎山也就更进一步了。

保持单项数据流

v-model是个语法糖,这个基础知识了。
更新的时候我们可以用$emit('update:value',newValue)这样就实现了父组件的数据更新

下面我列举了两种方式实现保持单项数据流的写法:

function

  1. 通过子组件再次通过双休数据绑定,将更新的update提取成公共方法,使用$emit('update:value',newValue)来实现父组件修改数据保持单项数据流。
  2. 比较不足的是子组件不能直接使用v-model语法糖

子组件改造

<template>
  <div style="width: 200px">
    <el-form label-position="top" label-width="80px" :model="value">
      <el-form-item label="名称">
        <el-input 
          :model-value="value.name" 
          @update:model-value="$event => handleUpdate('name', $event)">
        </el-input>
      </el-form-item>
      <el-form-item label="活动区域">
        <el-select 
          :model-value="value.region" 
          @update:model-value="$event => handleUpdate('region', $event)" 
          placeholder="活动区域">
          <el-option label="上海" value="shanghai"></el-option>
          <el-option label="北京" value="beijing"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="即时配送" prop="delivery">
        <el-switch 
          :model-value="value.delivery" 
          @update:model-value="$event => handleUpdate('delivery', $event)">
        </el-switch>
      </el-form-item>
      <el-form-item label="活动形式">
        <el-input 
          :model-value="value.type" 
          @update:model-value="$event => handleUpdate('type', $event)">
        </el-input>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
const _that = {}
export default {
  props: {
    value: {
      type: Object,
      default: ''
    }
  },
  methods: {
    /**
     * @description: 双向绑定的update更新函数
     * @param {*} name  自定义更新的字段名
     * @param {*} val   更新的内容
     * @return {*}
     */
    handleUpdate(name, val) {
      this.$emit('update:value', {
        ...this.value,
        [name]: val
      })
    }
  }
}
</script>

computed and Proxy

  1. 利用computed自带的gettersetter方法
  2. Proxy实现改变computed监听到具体的setter方法
  3. 优势是不用二次绑定了,子组件可以直接采用v-model

不使用Proxy需要每个单独写一个计算属性,因为computed监听不到深层的变化(计算属性是对象,改变属性不能触发setter方法),所有配合着Proxy使用效果更佳。

子组件改造

<template>
  <div style="width: 200px">
    <el-form label-position="top" label-width="80px" :model="formData">
      <el-form-item label="名称">
        <el-input v-model="formData.name"></el-input>
      </el-form-item>
      <el-form-item label="活动区域">
        <el-select v-model="formData.region" placeholder="活动区域">
          <el-option label="上海" value="shanghai"></el-option>
          <el-option label="北京" value="beijing"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="即时配送" prop="delivery">
        <el-switch v-model="formData.delivery"></el-switch>
      </el-form-item>
      <el-form-item label="活动形式">
        <el-input v-model="formData.type"></el-input>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: Object,
      default: ''
    }
  },
  computed: {
    formData: {
      get() {
        let _this = this
        return new Proxy(this.value,{
          set(obj,name,val) {
            _this.$emit('update:value', {
              ...obj,
              [name]: val
            })
            return true
          }
        }) 
      }
    }
  }
}
</script>

优雅,写代码一定要优雅!

Logo

前往低代码交流专区

更多推荐