前言????

  • 用户就是上帝,站在上帝的角度也就是站在使用者的角度去看待组件。
  • 用过不少优秀的UI库,用的时候美滋滋,轮到自己搭组件库的时候往往会去参考别人的源码。
  • 看完源码后恍然大悟 噢!原来可以这样写,但心里难免会有疑惑别人是怎么想出来这种解决思路的?????
  • 这一系列文章主要是面向未理解或者有疑惑的同学所以讲的比较基础,就让我们站在用户的角度去思考结构,看看换一种思路去写代码是不是有变化?

关于Radio组件????

为什么我们会用到radio

作为用户????‍????

  • 在填写表单的时候,作为一个系统的使用者,我们下意识的习惯靠简单的鼠标点击或者手指触碰就可以选择你的答案而不是还要在键盘敲打文字。
  • 为什么?还不是为了方便,相信没人会愿意用十分钟来填写全是input输入框的调查问卷吧?这也是为什么基本上所有调查问卷都是单选框和多选框的原因。
  • 说白了就是便携方便点一下即可。

作为组件库使用者????‍????

  • 当我们将组件库的radio组件放到我们的页面我们想要的效果是什么?
  • 不花里胡哨
  • 可以满足基本的选中需求
  • 可以在基本的需求上进行定制增加功能(比如:​​禁用​​​​大小​​​​图标​​ )
  • 说白了就是我能写尽量少的代码来配置。

搭建组件⚒️


接下来可能用尽可能少的代码搭配​​element​​​的源码进行结构说明,配合​​element Radio​​源码食用更加美味喔


基本架子????

  • 如果想要好看的​​radio​​​或者说是自定义的​​radio​​​组件那必不可能使用原生的​​radio​​直接放到页面上,所以要将整体架子进行改造。
  • 一个基本的​​radio​​​架子主要有两个点,一个是原生的​​input​​用来接收被点击事件,另一个就是制造给用户看的一个圆圈和一个文字展示区,大概是这样子的。
<template>
<label class="zl-radio">
<span class="zl-radio__input">
<span class="zl-radio__inner"></span>
<input
ref="radio"
type="radio"
class="zl-radio__original"
>
</span>
<span>
<slot></slot>
</span>
</label>
</template>
  • 可以看到在上面的代码中用​​label​​​将所有包住是为了让用户不管选中按钮还是文字都可以选中,而​​label​​也巧妙地运用了隐式关联
  • 隐式的方式是把需要绑定的标签放到​​label​​​内部,让​​label​​​包裹上这个需要绑定的元素就相当于点击了​​radio​
  • 上面那块​​span​​​是表示前面的圆圈样式,​​inner​​​类则是中间那个实心的样式,​​original​​​类将​​input​​​用​​opacity: 0;​​进行隐藏处理
  • 下面的​​span​​大家应该也很熟悉就是一个插槽来接受外部的文字

双向绑定????

  • 对于组件库使用者来说我们希望通过传入一个​​v-model​​​ 和​​label​​来控制我们表单的属性

「站在上帝的角度」谈谈Element组件结构-Radio_前端

  • 为了接收​​label​​​,架子很简单只需要用​​props​​​来接收即可,那​​v-model​​​传入的值呢?我们都知道​​v-model​​其实也是语法糖,上面的代码也相当于
<l-radio label="第一个按钮" v-bind:value="radio" v-on:input="radio=$event"></l-radio>
<l-radio label="第二个按钮" v-bind:value="radio" v-on:input="radio=$event"></l-radio>
  • 那么我们需要在子组件中​​props​​​增加一个​​value​​​来接收传入的值,而在子组件中可以使用​​this.$emit('input', val)​​​触发父组件上的​​input​​事件来改变选中
<script>
export default {
props: {
value: {},
label:{},
},
computed:{
model: {
get() {
return this.value;
},
set(val) {
this.$emit('input', val);
}
},
}
};
</script>
  • 在​​props​​​接收传入的​​v-model​​​的数值和​​label​​​的数值,因为我们需要在子组件调用父组件的​​input​​​事件,所以我们可以写一个计算属性分别用​​get​​​接收父组件通过双向绑定传下来的​​value​​​,通过​​set​​​设置新的值,对应的​​template​​如下
<template>
<label class="zl-radio">
<span
class="zl-radio__input"
:class="{
'is-checked': model === label
}"
>
<span class="zl-radio__inner"></span>
<input
ref="radio"
v-model="model"
:value="label"
type="radio"
class="zl-radio__original"
>
</span>
<span>
<slot></slot>
<template v-if="!$slots.default">{{ label }}</template>
</span>
</label>
</template>
  • 可以看到第一个​​span​​​在样式上使用动态​​class​​​如果使用者设置的​​label​​​与传入的​​value​​​相同时(即选中)则会有​​is-checked​​的选中样式
  • 在​​input​​​标签里面也使用了​​v-model​​​对上述的计算属性​​model​​​来做一个双向绑定,这样的话我们子组件的​​input​​的值就跟外部的表单属性进行连接起来了也就实现了父组件与子组件的双向绑定
  • 因为有时候这个单选按钮的值就是那个文字的值所以在插槽下面加了一个​​template​​​用于判断插槽中是否有文字时​​label​​的展现


由于本次只是分析结构,关于样式相关可以到这里查看​​传送门​


更多属性需求????

  • 至此一个最简单​​Radio​​组件就完成了,实现了双向绑定
  • 但我们往往还需要一些定制比如说禁用按钮的实现,我们希望通过在组件上添加属性来实现

「站在上帝的角度」谈谈Element组件结构-Radio_前端_02

  • 对此我们要在子组件做的处理也是一样的​​props​​​接收参数然后如果为​​true​​​做些什么处理如果​​false​​做些什么处理
...
<span
class="zl-radio__input"
:class="{
'is-checked': model === label,
'is-disabled': disabled
}"
>
<span class="zl-radio__inner"></span>
<input
ref="radio"
v-model="model"
:value="label"
type="radio"
class="zl-radio__original"
:disabled="disabled"
>
</span>
...
  • 就拿禁用来说我们拿到了属性就跟上面的​​is-checked​​​一样做动态样式绑定禁用的样式再让子组件​​input​​​的​​disabled​​同步处理
  • 一种属性是这样其他属性也是一样的道理就像​​Element​​​也定义了很多的动态类来做匹配包括​​边框​​​、​​尺寸​​等等

「站在上帝的角度」谈谈Element组件结构-Radio_Vue.js_03

写在最后????

  • 对于组件库的搭建我也在慢慢的摸索,讲的都是我自己得出来的分享所以说可能对于大佬来说会比较基础,但我相信我的不断输出可以帮助到一些有疑惑的同学。
  • 如果您觉得这篇文章有帮助到您的的话不妨????关注+点赞+收藏+评论+转发????支持一下哟~~????