👋 前言
- 用户就是上帝,站在上帝的角度也就是站在使用者的角度去看待组件。
- 用过不少优秀的
UI
库,用的时候美滋滋,轮到自己搭组件库的时候往往会去参考别人的源码。 - 看完源码后恍然大悟 噢!原来可以这样写,但心里难免会有疑惑别人是怎么想出来这种解决思路的?🤳
- 这一系列文章主要是面向未理解或者有疑惑的同学所以讲的比较基础,就让我们站在用户的角度去思考结构,看看换一种思路去写代码是不是有变化?
🔢 关于InputNumber组件
🙇 为什么我们会用到InputNumber
👨💼 作为用户
- 这个
InputNumber
其实大家经常可以看到,一般在于商品或者数量进行计算的时候出现,用户是更希望有这种控件来使用的。 - 为了满足人的
惰性
可以看到在任何一个奶茶点单系统或者商场购物车都会有这种控件来使用,无非就是为了方便。
👨💻 作为组件库使用者
- 我们可以看到很多的组件库都有
InputNumber
,这个计数器给开发者直接提供了UI
和算术功能,可以帮助开发者省很多时间。 - 当我们将组件库的
InputNumber
组件放到我们的页面我们想要的效果是什么?
- 可以简单明了看到我们的数字
number
- 可以满足基本加减需求
- 可以实现双向绑定
- 可以在基本的需求上进行定制增加功能(比如:
禁用
最大最小值
数字格式
)
⚒️ 搭建组件
接下来可能用尽可能少的代码搭配
element
的源码进行结构说明,配合element InputNumber源码食用更加美味喔
🔨 基本架子
网络异常,图片无法展示
|
- 要设计一个上图这种的
InputNumber
不难,我们会需要一个容器里面装着一个input组件
然后左右分别定位一个操作按钮代表+
-
,通过控制按钮来改变计数器的值。 - 总结起来总共也只有
4
个要点
- 准备一个外部的容器包含一个
input
组件 - 准备两个按钮分别定位在外部容器的左右,中间预留位置给数字。
- 通过控制加减来实现改变数字的值
- 双向绑定组件外的值和子组件数字的值
网络异常,图片无法展示
|
<div class="zl-input-number"> <span class="zl-input-number__decrease" role="button"> <i class="zl-icon-jian_sekuai"></i> </span> <span class="zl-input-number__increase" role="button"> <i class="zl-icon-jia_sekuai"></i> </span> <l-input ref="input" :value="displayValue"> </l-input> ... props:{ max: { type: Number, default: Infinity }, min: { type: Number, default: -Infinity }, value: {}, }
- 以上就是
element
最简单的InputNumber
结构,可以看到子组件接受一个max
和min
来控制数字的最大最小值,再用一个div
包住两个按钮和一个input
组件,两个按钮样式进行了定位处理,这里我个人的组件用了个人的加减图标➕ ➖来代表具体样式可以看element 样式 - 当然这只是一个架子我们还需要加上点击事件控制数字的加减
🎶 双向绑定
- 我们需要组件内部跟外部的字段绑定在一起,这时候就需要用到外面的
v-model
了 - 在父组件我们用
v-model
传入了一个值, 而v-model
的语法糖会把这个值当成props
的value
传到子组件,子组件只要通过$emit
时间改变外部的input
事件就可以啦。
data() { return { currentValue: 0 }; }, computed:{ displayValue() { let currentValue = this.currentValue; return currentValue; } }, watch:{ value: { immediate: true, handler(value) { let newVal = value === undefined ? value : Number(value); if (newVal >= this.max) newVal = this.max; if (newVal <= this.min) newVal = this.min; this.currentValue = newVal; this.$emit('input', newVal); } } } 复制代码
- 在这里我们内部监听外部组件通过
v-model
传进来的value
值来动态改变内部的input
组件的value
值displayValue
。 - 在
watch
中也对currentValue
做了限时,让他不超过最大值和最小值,通过this.$emit('input', newVal)
来改变外部的值,实现了双向绑定。
🧨 设置事件
- 此时我们的组件可以绑定外部但是还需要做一个加减事件。
<template> <div class="zl-input-number"> <span ... @click="decrease"> <i class="zl-icon-jian_sekuai"></i> </span> <span ... @click="increase"> <i class="zl-icon-jia_sekuai"></i> </span> <l-input ... @change="handleInputChange"> </l-input> </div> </template> 复制代码
- 我们需要给左右两个按钮添加事件,在
element
中使用了防连续点击,我们这里就用普通的点击事件代替。 - 给
input
组件也添加一个事件,这个组件内部已经做了相应的处理如果监听到外部数字变化会执行这个handleInputChange
方法
methods:{ handleInputChange(value) { const newVal = value === '' ? undefined : Number(value); if (!isNaN(newVal) || value === '') { this.setCurrentValue(newVal); } }, setCurrentValue(newVal) { const oldVal = this.currentValue; if (newVal >= this.max) newVal = this.max; if (newVal <= this.min) newVal = this.min; if (oldVal === newVal) return; this.$emit('input', newVal); this.$emit('change', newVal, oldVal); this.currentValue = newVal; }, decrease() { let newVal = this.value || 0; newVal--; this.setCurrentValue(newVal); }, increase() { let newVal = this.value || 0; newVal++; this.setCurrentValue(newVal); } } 复制代码
- 在这里我们通过
decrease
increase
来控制当前数字的加减然后通过setCurrentValue
来改变当前的值实现与input
组件内部的双向绑定。
🧮 更多需求
- 一个架子搭好了接下来就可以定制我们的组件了。
- 比如说禁用啊,更换尺寸啊,相信大家也已经很熟悉了,无非就是通过获取通过
props
实现动态样式切换即可。 - 当然
element
的InputNumber
也做了一些小数或者负数的处理,一个架子搭好了剩下就是填充内容了,发挥自己的想像来定制自己的组件,这次的组件结构就分享到这里啦,更多的实现可以参考传送门进行学习~
👋 写在最后
- 总的来说
InputNumber
组件相对于其他复杂组件比较简单,难点在于如何控制外部组件与内部InputNumber
组件的关联,把这个关联搞定也就没什么问题了。 - 对于组件库的搭建我也在慢慢的摸索,讲的都是我自己得出来的分享所以说可能对于大佬来说会比较基础,但我相信我的不断输出可以帮助到一些有疑惑的同学。
- 如果您觉得这篇文章有帮助到您的的话不妨🍉关注+点赞+收藏+评论+转发🍉支持一下哟~~😛