UI 组件二次封装之 props 传递与 v-model 实现

UI 组件二次封装之 props 传递与 v-model 实现

我们知道在组件的二次封装的时候,有三点是必须做到的:原组件的 props 继承、事件传值的回掉函数、v-model 双向绑定。

本文以 elemnetUI 为例,记录原组件的 props 继承以及 v-model 双向绑定相关的解决方案。

一、props 继承

以 el-select 为例,我们知道它的 change 事件只能获取选项的 value,不能获取 label,如果我们要同时获取这两者就比较麻烦,我们可以简单的封装一下:

<template>
  <el-select v-bind="$props" @change="changeEmit" @visible-change='visibleChangeEmit'>
    <el-option
      v-for="item in options"
      :key="item[propsAlias.value]"
      :label="item[propsAlias.label]"
      :value="item[propsAlias.label] + '·' + item[propsAlias.value]">
    </el-option>
  </el-select>
</template>

<script>
import { Select } from 'element-ui'
export default {
  name: 'em-select',
  props: {
    ...Select.props, // 这里继承原 UI 组件的 props
    // 配置
    options: {
      type: Array,
      default: () => [
        {
          value: '选项1',
          label: '黄金糕'
        },
        {
          value: '选项2',
          label: '双皮奶'
        }
      ]
    },
    // 数据项属性别名
    propsAlias: {
      type: Object,
      default: () => {
        return {
          value: 'value',
          label: 'label' 
        }
      }
    }
  },
  data() {
    return {}
  },
  methods: {
    changeEmit(val){
      this.$emit('change', val);    // 选中值发生变化时触发
    },
    visibleChangeEmit(isVisible){
      this.$emit('visible-change', isVisible);  // 下拉框出现/隐藏时触发
    },
  }
}
</script>

通过分隔符 · 我们把 label 和 value 都绑定到选项的值上,达到 change 事件可以同时获取的目的。

通过 ...Select.props 继承原组件的所有 props,同时我们可以在其下定义自己的 prop。

组件 props 的继承,核心代码是:

<template>
  <el-select v-bind="$props"></el-select>
</template>
<script>
import { Select } from 'element-ui'
export default {
  name: 'my-select'
  props: {
    ...Select.props, // 这里继承原 UI 组件的 props
  }
}
</script>

二、v-model 实现(方式一)

以穿梭框为例:

<template>
  <el-transfer class="em-transfer" v-bind="$props" v-model="currentValue" @change="changeVal"></el-transfer>
</template>
<script>
import { Transfer } from "element-ui";
export default {
  props: {
    ...Transfer.props,
    value: Array
  },
  name: "em-transfer",
  data() {
    return {};
  },
  methods:{
    changeVal(val, lr){
      this.$emit('change', val, lr); // 事件传值
    }
  },
  computed: {
    currentValue: {
      get: function() {
        return this.value;
      },
      set: function(newValue) {
        this.$emit("input", newValue); // 通过 input 事件更新 model
      }
    }
  },
};
</script>
<style lang="less" >
// 穿梭框样式调整
.el-transfer.em-transfer {
    ...
}
</style>

三、v-model 实现(方式二)

vue 组件中的 model 属性允许组件定制 v-model

还是以穿梭框为例:

<template>
  <el-transfer class="em-transfer" v-bind="$props" v-model="localVal" @change="changeVal"></el-transfer>
</template>
<script>
import { Transfer } from "element-ui";
export default {
  props: {
    ...Transfer.props,
    value: Array
  },
  name: "em-transfer",
  model:{
    prop: 'value',
    event: 'modelVal'  // 自定义方法,用来更新 model
  },
  data() {
    return {
      localVal: this.value 
    };
  },
  watch: {
    value(val){
      this.localVal = val; 
    }
  },
  methods:{
    changeVal(val, lr){
      this.$emit('modelVal', val);    // 更新 model
      this.$emit('change', val, lr);  // 事件传值
    }
  },
};
</script>
<style lang="less" >
// 穿梭框样式调整
.el-transfer.em-transfer {
  ...
}
</style>

使用方式同原组件一致:

<em-transfer
    v-model="dbServiceList"
    
    :titles="['可用物理数据库', '已选择']"
    :props="{
		key: 'id',
		label: 'label'
	}"
    :data="dbServices"
    @change="changeDbList"
    ></em-transfer>

相当于:

<em-transfer
    :value="dbServiceList"
    @modelVal="(val) => { dbServiceList = val }"
   
    :titles="['可用物理数据库', '已选择']"
    :props="{
		key: 'id',
		label: 'label'
	}"
    :data="dbServices"
    @change="changeDbList"
    ></em-transfer>
展开阅读全文

没有更多推荐了,返回首页