> 文档中心 > 使用Vue封装一个radio组件、RadioGroup组件

使用Vue封装一个radio组件、RadioGroup组件


一、封装radio组件

使用Vue封装一个radio组件、RadioGroup组件

  1. radio组件基本框架和样式:
<template>  <label class="one-radio" :class="{'is-checked':label == value}">      <span class="one-radio_input">   <!--  --><input type="radio" class="one-radio_label" :value="label" :name="name"v-model="model">   <!--  -->      </span>      <span class="one-radio_label">   <slot></slot>      <template v-if="!$slots.default">{{label}}</template>      </span>  </label></template><script>// 定义一个value属性,value值是接收到的labelexport default {    name: 'oneRadio',    data(){ return{ }    },    props:{ label: {     type: String,     default:'0' }, value: {     type: String,     default: '' }, name :{     type: String,     default: '' }    },    computed: { // 如果用双向绑定一个计算属性,必须提供一个get和set;需要写成一个对象 model:{     get(){  // model的值是父组件传过来的value  console.log('value');  return this.value     },     set(value){  // 触发父组件给当前组件注册的input事件  this.$emit('input',value)     } }    }}</script><style lang="scss" scoped>.one-radio{    color: #606266;    font-weight: 500;    line-height: 1;    position: relative;    cursor: pointer;    display: inline-block;    white-space: nowrap;    outline: none;    font-size: 14px;    margin-right: 30px;    -moz-user-select: none;    -webkit-user-select: none;    -moz-user-select: none;    .one-radio_input{      white-space: nowrap;      cursor: pointer;      outline: none;      display: inline-block;      line-height: 1;      position: relative;      vertical-align: middle;      .one-radio_inner{ border: 1px solid #dcdfe6; border-radius: 100%; width: 14px; height: 14px; background-color: #fff; position: relative; cursor: pointer; display: inline-block; box-sizing: border-box; &:after{   width: 4px;   height: 4px;   border-radius: 100%;   background-color: #fff;   content: "";   position: absolute;   left: 50%;   top: 50%;   transform: translate(-50%,-50%) scale(0);   transition: transform .15s ease-in; }      }      .one-radio_original{ opacity: 0; outline: none; position: absolute; z-index: -1; top: 0; left: 0px; right: 0; bottom: 0; margin: 0;      }    }    .one-radio_label{      font-size: 14px;      padding-left: 10px;;    }  }  // 选中的样式  .one-radio.is-checked{    .one-radio_input{      .one-radio_inner{ border-color: #409eff; background-color: #409eff; &:after{   transform: translate(-50%,-50%) scale(1); }      }    }    .one-radio_label{      color:#409eff;    }  }</style>
  1. radio组件的数据双向绑定:
    需要父组件传递label值和value值,其中value值使用v-model来绑定。
 <one-radio    v-model="gender" label="0"></one-radio>    <one-radio    v-model="gender" label="1"></one-radio>

子组件接收数据后,对数据进行处理:
①当radio组件被点击时,绑定的数据应该变为该组件的label值,因此我们将子组件中的input标签的value属性绑定为传入的label值;并声明一个计算属性model双向绑定到input组件上(model通过get获取值–>父组件传过来的value,通过set方法将值回调给父组件)。
②改变样式:
当我们在点击radio组件时,让被选中的组件添加选中样式,可以通过label和value的比较来判断,:class="{'is-checked':label == value}",如果相同,则显示选中样式。

二、封装RadioGroup组件

因为存在两种①radio不被RadioGroup包裹和②radio被RadioGroup包裹的情况。第二种情况,如果radio被RadioGroup包裹时,我们在上面封装的组件就不能用了。因此,我们需要考虑radio是否被RadioGroup所包裹。
为了知晓radio是否被包裹,可以使用provide方法(在祖先组件中负责向子孙组件发送数据)和inject(在子孙组件中负责接收来自祖先组件的数据)。

在redio组件中,添加inject 来接收传入的值;
而且,我们想要拿到value值,并一定是从父组件中拿到(使用this.value),有可能是从祖先组件中拿到value值,因此,我们在computed计算属性中添加一个isGroup方法,用于判断是否radio被radioGroup所包裹。
当radio被radioGroup包裹时,不能用this.value,只能用this.radioGroup.value,因此,return this.isGroup ? this.RadioGroup.value : this.value;,根据isGroup来决定如何获取value的值,修改值set中相应地也要进行修改,this.isGroup ? this.RadioGroup.$emit('input',value) : this.$emit('input',value)

<template>  <label class="one-radio" :class="{'is-checked':label == value}">      <span class="one-radio_input">   <!--  --><input type="radio" class="one-radio_label" :value="label" :name="name"v-model="model">   <!--  -->      </span>      <span class="one-radio_label">   <slot></slot>      <template v-if="!$slots.default">{{label}}</template>      </span>  </label></template><script>// 定义一个value属性,value值是接收到的labelexport default {    name: 'oneRadio',    data(){ return{ }    },    inject: {      RadioGroup:{ default: ''      }    },    computed: { // 如果用双向绑定一个计算属性,必须提供一个get和set;需要写成一个对象 model:{     get(){  // model的值是父组件传过来的value  // return this.value  return this.isGroup ? this.RadioGroup.value : this.value;     },     set(value){  // 触发祖先组件RadioGroup给当前组件注册的input事件  //this.$emit('input',value)   this.isGroup ? this.RadioGroup.$emit('input',value) : this.$emit('input',value)     } }, isGroup(){   // 用于判断radio是否被radioGroup所包裹(使用2个感叹号,将其改为布尔值)   return !!this.RadioGroup }    }}</script>

使用Vue封装一个radio组件、RadioGroup组件

使用Vue封装一个radio组件、RadioGroup组件
最终效果:
使用Vue封装一个radio组件、RadioGroup组件
使用Vue封装一个radio组件、RadioGroup组件


这里附上 完整代码

  1. radioGroup.vue:
<template>  <div class="one-radio-group">      <slot></slot>  </div></template><script>export default {    name:'HmRadioGroup',    provide(){ return {     RadioGroup: this }    },    props:{ value: null    },   }</script><style></style>
  1. radio.vue
<template>  <label class="one-radio" :class="{'is-checked':label === model}">      <span class="one-radio_input">   <!--  --><input type="radio" class="one-radio_label" :value="label" :name="name"v-model="model">   <!--  -->      </span>      <span class="one-radio_label">   <slot></slot>      <template v-if="!$slots.default">{{label}}</template>      </span>  </label></template><script>// 定义一个value属性,value值是接收到的labelexport default {    name: 'oneRadio',    data(){ return{ }    },    inject: {      RadioGroup:{ default: ''      }    },    props:{ label: {     type: String,     default:'0' }, value: {     type: String,     default: '' }, name :{     type: String,     default: '' }    },    computed: { // 如果用双向绑定一个计算属性,必须提供一个get和set;需要写成一个对象 model:{     get(){  // model的值是父组件传过来的value  console.log('value');  // return this.value  return this.isGroup ? this.RadioGroup.value: this.value     },     set(value){  // 触发父组件给当前组件注册的input事件  //  this.$emit('input',value) this.isGroup ? this.RadioGroup.$emit('input',value) : this.$emit('input',value)     } }, isGroup(){   // 用于判断radio是否被radioGroup所包裹(使用2个感叹号,将其改为布尔值)   return !!this.RadioGroup }    }}</script><style lang="scss" scoped>.one-radio{    color: #606266;    font-weight: 500;    line-height: 1;    position: relative;    cursor: pointer;    display: inline-block;    white-space: nowrap;    outline: none;    font-size: 14px;    margin-right: 30px;    -moz-user-select: none;    -webkit-user-select: none;    -moz-user-select: none;    .one-radio_input{      white-space: nowrap;      cursor: pointer;      outline: none;      display: inline-block;      line-height: 1;      position: relative;      vertical-align: middle;      .one-radio_inner{ border: 1px solid #dcdfe6; border-radius: 100%; width: 14px; height: 14px; background-color: #fff; position: relative; cursor: pointer; display: inline-block; box-sizing: border-box; &:after{   width: 4px;   height: 4px;   border-radius: 100%;   background-color: #fff;   content: "";   position: absolute;   left: 50%;   top: 50%;   transform: translate(-50%,-50%) scale(0);   transition: transform .15s ease-in; }      }      .one-radio_original{ opacity: 0; outline: none; position: absolute; z-index: -1; top: 0; left: 0px; right: 0; bottom: 0; margin: 0;      }    }    .one-radio_label{      font-size: 14px;      padding-left: 10px;;    }  }  // 选中的样式  .one-radio.is-checked{    .one-radio_input{      .one-radio_inner{ border-color: #409eff; background-color: #409eff; &:after{   transform: translate(-50%,-50%) scale(1); }      }    }    .one-radio_label{      color:#409eff;    }  }</style>
  1. App.vue 中使用
<template>  <div id="app">    <one-radio    v-model="gender" label="0"></one-radio>    <one-radio    v-model="gender" label="1"></one-radio>    <hm-radio-group v-model="gender">      <one-radio label="0"></one-radio>      <one-radio label="1"></one-radio>    </hm-radio-group>  </div></template><script>export default {  name: 'App',  components: {  },  data(){    return {      money: 100,      visible: false,      active: true,      password: '',      swFlag: false,      swFlag2: false,      gender:''    }  }  ,  mounted(){  },  methods:{    // 父组件在使用时定义自己的点击事件,其本质是子组件中的点击事件触发父组件中的点击事件    getInfo() {      console.log('获取信息!!'); // 获取子传父的信息    },    // 父组件控制dialog的显示与隐藏    // switchDialog(value){     //   this.visible = value    // },    db(value){      this.money = value    }  }}</script><style></style>