当前位置 主页 > 网站技术 > 代码类 >

    vue自定义指令实现仅支持输入数字和浮点型的示例

    栏目:代码类 时间:2019-10-30 18:07

    再开始本篇的讨论之前,先思考几个问题:

    使用html元素属性type='number'是否可以满足要求

    Vue中指令一般被设计用来操作dom元素的,而vue视图是基于数据模型的,如何在操作dom的同时,同时更新数据

    你定义的指令不能只能在input元素上使用,还要支持在其父元素上使用,自定义组件及第三方组件上使用

    你的指令是不是支持局部作用域,比如for循环渲染的数据的单元item,如何识别这个item进行数据更新和dom操作

    如何控制字符数目,超出禁止输入

    如何实现全局性的功能定义,从而在各个子组件中灵活使用

    还有没有别的优化替代方案

    问题思考

    可以肯定的是,针对方案1,答案是:可以。很明显,它只会作为我们此次讨论的一个噱头罢了!为什么呢?因为这种处理方案有兼容性,不说别的,拿谷歌和火狐浏览器对比来看:谷歌浏览器表现堪称完美,而火狐浏览器表现就狠差强人意。而且会衍生出各种各样的问题。这里不再赘述,有兴趣的可以自己试试看

    针对方案6,不是本次讨论的重点,但是思路方向很重要。比如使用vue的状态管理机制库vuex来解决这个数据流转的问题是不是可以!这里只是一个方向,感兴趣的同学可以去调研一下。

    实现方案

    Vue允许我们来定义全局指令,从而在各个子组件中使用。那我问题6我们解决了。那关键是如何实现问题2-5以及其相关的技术问题。比如我们定义指令onlyNum。

    1.1 指令宿主

    我们在使用指令时,指令的宿主元素不一定是input本身,也有可能使其父级或父级以上元素。那么我们如何来识别?

    // 只能输入整数
    onlyNum (el,binding,vnode) {
      let ele = el.tagName === 'INPUT' ? el : el.querySelector('input')
      ele.oninput = function() {
       //获取相关的指令配置信息
       let rel = vnode.data.directives.filter(item =>{
        return item.name === "only-num"
       })[0]
       vnode.context.$nextTick(()=>{
        handleInput(ele,vnode,rel)
       })
      }
    }

    如上所示,我们看到了一个el的参数,它是:指令所绑定的元素,可以用来直接操作 DOM。那么我们也就能通过这样一个宿主元素找到它下边的input元素了,从而不必关心当前是不是input元素不重要,who care!然后我们通过处理事件函数input,来开始操作dom和数据了。

    1.2 捕捉指令配置内容

    我们会在同一个宿主元素上绑定一个或者多个指令,但是,如何找到当前指令的配置呢?上如定义了一个rel的变量,返回了指令onlyNum的所有配置信息。

    1.3 数据更新如何与dom更新同步

    由于vue的数据渲染是异步的。因此当数据更新后,页面dom并不一定就会按照我们期望的那样来渲染。好在vue里提供了一套处理机制。

    虚拟节点vnode参数中有一个上下文对象context,它用来表示宿主对象所在的组件对象。那么借助$nextTick就可以实现数据更新后,dom跟着渲染。

    1.4 使用指令配置控制数据

    /**
     * [handleInput 在输入阶段的处理逻辑]
     * @param {[DOM]} ele  [当前指令操作的dom对象]
     * @param {[虚拟节点]} vnode [当前指令渲染的虚拟节点]
     * @param {[指令信息]} rel  [当前指令的所有指令信息]
     * @param {[校验类型]} type [输入阶段的校验类型]
     *   "number": 仅支持输入数字
     *   "float": 仅支持数字和小数点
     */
    function handleInput(ele,vnode,rel){
     let rule;
     switch(true) {
      case rel.modifiers.float: // 浮点型
       rule = /[^\d\.]/g; break;
      default: //默认仅支持输入数字
       rule = /\D/g;
     }
     let val = ele.value.replace(rule,"");
     let maxLen = vnode.data.attrs && vnode.data.attrs['max-len'] ? vnode.data.attrs['max-len'] :0;
     if(maxLen>0){val = val.substr(0,maxLen)}
     setValueWithExpressionVue({
      currObj:vnode.context.$data,
      expression:rel.expression,
      value:val,
      key:vnode.key,
      arg:rel.arg,
      toString:rel.modifiers.string || rel.modifiers.float
     })
    }