模仿element-ui封装vue组件库(radio,radio-group)
七、封装一个element-ui风格的radio组件
前置知识点:
radio的基本使用
参数支持:
参数名称 | 参数描述 | 参数类型 | 默认值 |
v-model | 双向绑定 | boolean | false |
label | 单选框和value值 | string,num,boolean | '' |
name | name属性 | string | '' |
7.1radio组件的基本框架和样式
框架、基本样式以及选中样式:
<template> <label class="ra-radio is-checke"> <span class="ra-radio_input"> <span class="ra-radio_inner">span> <input type="radio" class="ra-radio_original" > span> <span class="ra-radio_label">我是labelspan> label> template> <script> export default { name: 'ra-radio', props: {} }, watch: {}, data () { return {} }, methods: {} } script> <style lang="scss" scoped> .ra-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; .ra-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; } } .ra-radio_original{ opacity: 0; outline: none; position: absolute; z-index: -1; top: 0; left: 0px; right: 0; bottom: 0; margin: 0; } } .ra-radio_label{ font-size: 14px; padding-left: 10px;; } } // 选中的样式 .ra-radio.is-checked{ .ra-radio_input{ .ra-radio_inner{ border-color: #409eff; background-color: #409eff; &:after{ transform: translate(-50%,-50%) scale(1); } } } .ra-radio_label{ color:#409eff; } } style>
7.2radio组件的数据双向绑定
实现radio组件的数据双向绑定,除了要绑定数据本身外,还要控制radio组件的样式。
实现radio组件数据的绑定,需要父组件传递的label值和value值,其中value值使用v-model语法糖来绑定。
<ra-radio v-model="gender" label="0">男ra-radio> <ra-radio v-model="gender" label="1">女ra-radio>
子组件接收数据后,要对数据进行处理。
当radio组件被点击时,绑定的数据应该变为该组件的label值。我们将组件中的input标签的value绑定为传入的label值,并且声明一个计算属性model双向绑定到input组件上,model我们需要通过get方法获取值;并且通过set方法将值回调给父组件。
同时,当我们在点击radio组件时,我们应该让被选中的组件添加选中样式,我们通过label和value的比较来判断,如果相同则显示选中样式。
<template> <label class="ra-radio" :class="{'is-checked': label == value}"> <span class="ra-radio_input"> <span class="ra-radio_inner">span> <input type="radio" class="ra-radio_original" :value="label" v-model="model" > span> <span class="ra-radio_label"> <slot>slot> <template v-if="!$slots.default">{{label}}template> span> label> template> //计算属性 computed: { model: { get () { return this.value }, set (value) { // 触发父组件的input事件 this.$emit('input', value) } } },
八、封装一个element-ui风格的radio-group组件
radio-group组件是再radio组件上进行优化的,它的目的是在我们使用radio组件时,不必给每个组件都添加一个v-model,而是通过绑定一个v-model来实现数据绑定。
使用radio-group组件包裹radio组件时,需要考虑到的一个问题就是radio-group组件于radio组件之间的通讯。我们在使用radio-group组件时将数据通过v-model进行了绑定,那么raido组件就不能直接拿到这个值,所以我们需要使用provide/inject进行祖孙组件之间得传值。
使用provide/inject非常简单,在radio-group中通过声明provide对象将组件自身进行传递,在radio中使用inject进行接收即可。
radio-group组件架构:
<template> <div class="ra-radio-group"> <slot>slot> div> template> <script> export default { name: 'ra-radio-group', provide () { return { RadioGroup: this } }, props: { // 组件接收到了value值,我们需要触发这个组件的input事件 // provide 与 inject 用来做祖孙之间得组件通讯 value: null } } script>
在radio组件中,通过inject可以直接接收到参数,此时,原本通过v-model传递进来的value值,变成了radio-group组件传进来的RadioGroup.value值,所以在computed计算属性中,我们先写一个radio组件是否被radio-group组件进行判断的方法,并且使用在model中,如果被包裹了,则使用RadioGroup.value值,否则使用value值。
同时在is-checked类的判断上抛弃label于value的比较,转而通过label于model(model此时的值为value或RadioGroup.value)比较,来进行样式的更改。
<template> <label class="ra-radio" :class="{'is-checked': label == model}"> <span class="ra-radio_input"> <span class="ra-radio_inner">span> <input type="radio" class="ra-radio_original" :value="label" v-model="model" > span> <span class="ra-radio_label"> <slot>slot> <template v-if="!$slots.default">{{label}}template> span> label> template> <script> export default { name: 'ra-radio', props: { label: { type: [String, Number, Boolean], defualt: '' }, value: null, name: { type: String, defualt: '' } }, inject: { RadioGroup: { default: '' } }, computed: { model: { get () { return this.isGroup ? this.RadioGroup.value : this.value }, set (value) { // 触发父组件的input事件 this.isGroup ? this.RadioGroup.$emit('input', value) : this.$emit('input', value) } }, // 用于判断radio是否被radioGroup包裹 isGroup () { return !!this.RadioGroup } } } script>
--------------------------------------------至此,radio组件与radio-group组件封装完毕--------------------------------------------------
附radio组件代码:
<template> <label class="ra-radio" :class="{'is-checked': label == model}"> <span class="ra-radio_input"> <span class="ra-radio_inner">span> <input :value="label" :name="name" v-model="model" type="radio" class="ra-radio_original" > span> <span class="one-radio_label"> <slot>slot> <template v-if="!$slots.default"> {{label}} template> span> label> template> <script> export default { name: 'ra-radio', inject: { RadioGroup: { default: '' } }, data () { return { } }, props: { label: { type: [String, Number, Boolean], default: '' }, value: null, name: { type: String, default: '' } }, computed: { model: { get () { return this.isGroup ? this.RadioGroup.value : this.value }, set (value) { // 触发父组件的input事件 this.isGroup ? this.RadioGroup.$emit('input', value) : this.$emit('input', value) } }, // 用于判断radio是否被radioGroup包裹 isGroup () { return !!this.RadioGroup } } } script> <style lang="scss" scoped> .ra-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; .ra-radio_input{ white-space: nowrap; cursor: pointer; outline: none; display: inline-block; line-height: 1; position: relative; vertical-align: middle; .ra-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; } } .ra-radio_original{ opacity: 0; outline: none; position: absolute; z-index: -1; top: 0; left: 0px; right: 0; bottom: 0; margin: 0; } } .ra-radio_label{ font-size: 14px; padding-left: 10px;; } } // 选中的样式 .ra-radio.is-checked{ .ra-radio_input{ .ra-radio_inner{ border-color: #409eff; background-color: #409eff; &:after{ transform: translate(-50%,-50%) scale(1); } } } .ra-radio_label{ color:#409eff; } } style>
附radio-group组件代码:
<template> <div class="ra-radio-group"> <slot>slot> div> template> <script> export default { name: 'ra-radio-group', provide () { return { RadioGroup: this } }, props: { // 组件接收到了value值,我们需要触发这个组件的input事件 // provide 与 inject 用来做祖孙之间得组件通讯 value: null } } script> <style lang="scss" scoped> style>