Element-UI 源码简析——inputNumber

2,014 阅读2分钟

26d8116a7588ac4a91a8398280e436fe.png

序言

这一篇的话 , 我直接挑InputNumber下手了,跳过了Input , 直接就拿InputNumber来讲, 感觉InputNumber比较简单,废话就不多说了,直接开始吧。

结构分析

老样子还是根据三个角度来分析,Dom、数据属性、事件。

慢慢的发现,以这样的角度来分析的话 还可以,如果有多的东西的话 可以慢慢加。

Dom

    <div>
        <span class="el-input-number__decrease">
            <i></i>
        </span>
        <span class="el-input-number__increase">
            <i></i>
        </span>
        <el-input></el-input>
    </div>
    
   
    <div
        @dragstart.prevent
        :class="[
            'el-input-number',
            inputNumberSize ? 'el-input-number--' + inputNumberSize : '',
            { 'is-disabled': inputNumberDisabled },
            { 'is-without-controls': !controls },
            { 'is-controls-right': controlsAtRight },
        ]"
    >
        <span
            class="el-input-number__decrease"
            role="button"
            v-if="controls"
            v-repeat-click="decrease"
            :class="{ 'is-disabled': minDisabled }"
            @keydown.enter="decrease"
        >
            <i :class="`el-icon-${controlsAtRight ? 'arrow-down' : 'minus'}`"></i>
        </span>
        <span
            class="el-input-number__increase"
            role="button"
            v-if="controls"
            v-repeat-click="increase"
            :class="{ 'is-disabled': maxDisabled }"
            @keydown.enter="increase"
        >
            <i :class="`el-icon-${controlsAtRight ? 'arrow-up' : 'plus'}`"></i>
        </span>
        <el-input
            ref="input"
            :value="displayValue"
            :placeholder="placeholder"
            :disabled="inputNumberDisabled"
            :size="inputNumberSize"
            :max="max"
            :min="min"
            :name="name"
            :label="label"
            @keydown.up.native.prevent="increase"
            @keydown.down.native.prevent="decrease"
            @blur="handleBlur"
            @focus="handleFocus"
            @input="handleInput"
            @change="handleInputChange"
        >
        </el-input>
    </div>

Dom 这块的话 倒是没什么特别的结构就是正常的 增加、减少、数量的结构。

外层div (el-input-number) 属性

  • @dragstart.prevent : 禁止了div的默认拖动行为 不设置中间的数字底色可以被拖动 小细节!! 很不错

  • class :

    --inputNumberSize ? 'el-input-number--' + inputNumberSize : 计数器尺寸

    --is-disabled': inputNumberDisabled : 禁用

    --is-without-controls : !controls : 是否使用控制按钮

    --is-controls-right : controlsAtRight : 控制按钮位置

增加 递减 (el-input-number__decrease || el-input-number__increase) 两个属性基本一样

  • role="button" 之前说过的为盲人提供的访问网络的便利程序

  • v-if="controls" 是否使用控制按钮

  • v-repeat-click="decrease" 替换了原有的点击事件 使用了自定义指令(directives)来实现 知识点!

  • :class="{ 'is-disabled': minDisabled }" 禁用

  • @keydown.enter="decrease" 键盘回车事件

展示的数字

  • :value="displayValue" 绑定的value值

  • :placeholder="placeholder" 输入框默认 placeholder

  • :disabled="inputNumberDisabled" 是否禁用计数器

  • :size="inputNumberSize" 计数器尺寸

  • :max="max" 最大值

  • :min="min" 最小值

  • :name="name" 原生属性

  • :label="label" 输入框关联的label文字

  • @keydown.up.native.prevent="increase" 上键

  • @keydown.down.native.prevent="decrease" 下键

  • @blur="handleBlur" 失焦

  • @focus="handleFocus" 聚焦

数据属性

props data


    props: {
        step: {  // 计数器步长 每次di
            type: Number,
            default: 1,
        },
        stepStrictly: { // 只能输入 step 的倍数  严格步数
            type: Boolean,
            default: false,
        }, 
        max: { //设置计数器允许的最大值
            type: Number,
            default: Infinity,
        },
        min: {  //设置计数器允许的最小值
            type: Number,
            default: -Infinity,
        },
        value: {}, //绑定值
        disabled: Boolean, //是否禁用计数器
        size: String, //计数器尺寸
        controls: { //是否使用控制按钮
            type: Boolean,
            default: true,
        },
        controlsPosition: { //控制按钮位置
            type: String,
            default: "",
        },
        name: String, //原生属性
        label: String, //输入框关联的label文字
        placeholder: String, //输入框默认 placeholder
        precision: { //数值精度
            type: Number,
            validator(val) {
                return val >= 0 && val === parseInt(val, 10);
            },
        },
    },
    data() {
        return {
            currentValue: 0, // 当前的值
            userInput: null, // 上次的值
        };
    },

watch

 watch: {
      value: {
        //确认是否以当前的初始值执行handler的函数。
        immediate: true,
        handler(value) {
          //Number() 函数把对象的值转换为数字。
          let newVal = value === undefined ? value : Number(value);
          if (newVal !== undefined) {
            if (isNaN(newVal)) {
              return;
            }
            if (this.precision !== undefined) {
              //如果数值精度存在,将数字按精度转换
              newVal = this.toPrecision(newVal, this.precision);
            }
          }
          if (newVal >= this.max) newVal = this.max;
          if (newVal <= this.min) newVal = this.min;
          this.currentValue = newVal;
          this.$emit('input', newVal);
        }
      }
    },

事件

          //按精度转换数值
        toPrecision(num, precision) {
            if (precision === undefined) precision = this.numPrecision;
            //toFixed() 方法可把 Number 四舍五入为指定小数位数的数字,返回字符串;parseFloat()函数可解析一个字符串,并返回一个浮点数。
            return parseFloat(Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision));
        },
        //获取value的小数位数
        getPrecision(value) {
            if (value === undefined) return 0;
            const valueString = value.toString();
            const dotPosition = valueString.indexOf(".");
            let precision = 0;
            if (dotPosition !== -1) {
                precision = valueString.length - dotPosition - 1;
            }
            return precision;
        },
        //返回value增加step后的值
        _increase(val, step) {
            if (typeof val !== "number" && val !== undefined) return this.currentValue;

            const precisionFactor = Math.pow(10, this.numPrecision);
            return this.toPrecision((precisionFactor * val + precisionFactor * step) / precisionFactor);
        },
        //返回value减去step后的值
        _decrease(val, step) {
            if (typeof val !== "number" && val !== undefined) return this.currentValue;
            const precisionFactor = Math.pow(10, this.numPrecision);
            return this.toPrecision((precisionFactor * val - precisionFactor * step) / precisionFactor);
        },
        // 点击增加按钮
        increase() {
            if (this.inputNumberDisabled || this.maxDisabled) return;
            const value = this.value || 0;
            const newVal = this._increase(value, this.step);
            this.setCurrentValue(newVal);
        },
        // 点击递减按钮
        decrease() {
            if (this.inputNumberDisabled || this.minDisabled) return;
            const value = this.value || 0;
            const newVal = this._decrease(value, this.step);
            this.setCurrentValue(newVal);
        },
        // 中间input 失焦事件
        handleBlur(event) {
            this.$emit("blur", event);
        },
        // 中间input 聚焦事件
        handleFocus(event) {
            this.$emit("focus", event);
        },
        // 用来设置 中间所显示的数字的值
        setCurrentValue(newVal) {
            const oldVal = this.currentValue;
            if (typeof newVal === "number" && this.precision !== undefined) {
                newVal = this.toPrecision(newVal, this.precision);
            }
            if (newVal >= this.max) newVal = this.max;
            if (newVal <= this.min) newVal = this.min;
            if (oldVal === newVal) return;
            this.userInput = null;
            this.$emit("input", newVal);
            this.$emit("change", newVal, oldVal);
            this.currentValue = newVal;
        },
        // 中间input事件
        handleInput(value) {
            this.userInput = value;
        },
        handleInputChange(value) {
            const newVal = value === "" ? undefined : Number(value);
            if (!isNaN(newVal) || value === "") {
                this.setCurrentValue(newVal);
            }
            this.userInput = null;
        },
        select() {
            this.$refs.input.select();
        },

招聘广告

言重式招聘 寻人!!!寻志同道合之人、寻竭忠尽智之人、寻深思远虑之人、寻勤恳至诚之人
浙江大华技术股份有限公司-软研-智慧城市产品研发部招聘高级前端!!!!!
欢迎大家来聊,有意向可发送简历到chen_zhen@dahuatech.com

尽量宽恕别人,而决不要原谅自己。——————无奖竞猜