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

简介: 「站在上帝的角度」谈谈Element组件结构-InputNumber

👋 前言


  • 用户就是上帝,站在上帝的角度也就是站在使用者的角度去看待组件。
  • 用过不少优秀的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结构,可以看到子组件接受一个maxmin来控制数字的最大最小值,再用一个div包住两个按钮和一个input组件,两个按钮样式进行了定位处理,这里我个人的组件用了个人的加减图标➕ ➖来代表具体样式可以看element 样式
  • 当然这只是一个架子我们还需要加上点击事件控制数字的加减


🎶 双向绑定

  • 我们需要组件内部跟外部的字段绑定在一起,这时候就需要用到外面的v-model
  • 在父组件我们用v-model传入了一个值, 而v-model的语法糖会把这个值当成propsvalue传到子组件,子组件只要通过$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组件的valuedisplayValue
  • 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);
     }
   }
复制代码
  • 在这里我们通过decreaseincrease来控制当前数字的加减然后通过setCurrentValue来改变当前的值实现与input组件内部的双向绑定。


🧮 更多需求

  • 一个架子搭好了接下来就可以定制我们的组件了。
  • 比如说禁用啊,更换尺寸啊,相信大家也已经很熟悉了,无非就是通过获取通过props实现动态样式切换即可。
  • 当然elementInputNumber也做了一些小数或者负数的处理,一个架子搭好了剩下就是填充内容了,发挥自己的想像来定制自己的组件,这次的组件结构就分享到这里啦,更多的实现可以参考传送门进行学习~


👋 写在最后


  • 总的来说InputNumber组件相对于其他复杂组件比较简单,难点在于如何控制外部组件与内部InputNumber组件的关联,把这个关联搞定也就没什么问题了。
  • 对于组件库的搭建我也在慢慢的摸索,讲的都是我自己得出来的分享所以说可能对于大佬来说会比较基础,但我相信我的不断输出可以帮助到一些有疑惑的同学。
  • 如果您觉得这篇文章有帮助到您的的话不妨🍉关注+点赞+收藏+评论+转发🍉支持一下哟~~😛
相关文章
|
3月前
|
调度
忙旋转:概念、用途及考量
【8月更文挑战第21天】
34 0
|
编译器 C#
【C#本质论 六】类-从设计的角度去认知(封装)(下)
【C#本质论 六】类-从设计的角度去认知(封装)(下)
87 0
|
存储 Java 程序员
【C#本质论 六】类-从设计的角度去认知(封装)(上)
【C#本质论 六】类-从设计的角度去认知(封装)(上)
109 0
|
编译器 C#
【C#本质论 七】类-从设计的角度去认知(继承)(下)
【C#本质论 七】类-从设计的角度去认知(继承)(下)
67 0
|
存储 开发框架 .NET
【C#本质论 七】类-从设计的角度去认知(继承)(上)
【C#本质论 七】类-从设计的角度去认知(继承)(上)
91 0
|
供应链 安全 智能硬件
|
设计模式 编解码 前端开发
换一个角度来审视React
a. React是什么 • 前端场景下MVC架构 • Flux设计模式 • Redux + React = MVC b. JSX • 手动实现一个JSX转换器
117 0
|
Web App开发 缓存 JavaScript
页面是如何生成的(宏观角度)
进程、线程 网页的主要进程 显示系统基础知识 渲染进程的线程 渲染进程主线程
|
JSON JavaScript 安全
从另外一个角度看待——JS深浅复制
1. 浅复制 VS 深复制 2. 浅谈浅复制 • 扩展运算符(...)复制对象和数组 • Object.assign() • Object.getOwnPropertyDescriptors()和Object.defineProperties() 3. 深复制 4. 掘金的文章
|
容器
「站在上帝的角度」谈谈Element组件结构-Input
「站在上帝的角度」谈谈Element组件结构-Input