vue.js自定义指令directive开发一个表单验证插件validate.js

2019-05-2319:46:23WEB前端开发Comments2,863 views字数 5212阅读模式

新项目框架采用vue-cli3和typescirpt搭建。因为项目比较轻量,所以基本没有使用额外的ui组件,有时候我们需要的一些基础组件我就直接自己开发了。今天就来介绍一下如何利用vue的自定义指令directive来开发一个表单验证插件的过程。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

1.vue插件开发

关于vue的插件开发,官方文档里有很清晰的说明,详情可以去阅读开发文档。我自己开发的表单验证插件validate.ts和loading,messageBox插件都是利用了这种方式。今天先来看表单验证插件的开发。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

  • vue全局指令
// myPlugin.js
export default {
  install: (Vue, options) => {
    // 注册一个my-directive指令
    Vue.directive('my-directive', {
      bind(el, binding, vnode, oldVnode) {
        // 逻辑
      }
      ...
    })
  }
}
// main.js
import Vue from 'vue';
import myPlugin from 'myPlugin';
Vue.use(myPlugin);

复制代码

上面是注册一个vue指令插件的写法。值得注意的是注册自定义指令的时候,bind()函数为指令的钩子函数,其中的参数el表示指令绑定的元素,可以直接操作DOM。binding表示一个对象,包括指令名称,绑定值等信息。vnode和oldVnode表示Vue编译生成的虚拟节点。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

我们通过注册一个全局指令v-validateParams指令,绑定到输入表单的input标签上来校验当前输入值是否符合要求。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

2.v-validateParams指令

最开始我参考了网上的一些代码。基础的实现如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

  • 整体框架
import Vue from 'vue'
export default {
  install: (Vue, options) => {
    // 注册一个全局自定义指令 `v-validateParams`
    Vue.directive('validateParams', {
      // 当被绑定的元素插入到 DOM 中时
      inserted: function (el, binding, vNode) {
        // 给指令绑定的Dom元素添加事件监听,监测输入框失焦事件
        // 每次当表单中的输入框失焦时执行函数
        el.addEventListener('blur', function (event) {
          // 1.首先重置所有错误提示
          // 2.获取自定义指令中传入的校验规则参数和表单输入的值
          // 3.依次判断当前输入的值是否符合校验规则
        })
      }
    })

    // 注册一个全局自定义指令 `v-validateSubmit`,这个指令绑定到表单的提交button上
    Vue.directive('validateSubmit', {
      // 当被绑定的元素插入到 DOM 中时
      inserted: function (el, binding, vNode) {
        // 给提交button添加事件监听
        el.addEventListener('click', function (event) {
          // 获取当前组件内所有含有v-check类名的元素
          let elements = vNode.context.$el.getElementsByClassName('v-check')
          var evObj = vNode.context.$el.createEvent('Event')
          evObj.initEvent('blur', true, true)
          for (let element of elements) {
            // 给所有v-check元素绑定blur事件
            element.dispatchEvent(evObj);
          }
          // 获取当前组件下的所有错误提示元素
          let errorInputs = vNode.context.$el.getElementsByClassName('input-error');
          // 如果组件中没有错误提示元素,则执行当前组件实例中的submit()函数
          if(errorInputs.length === 0){
            vNode.context.submit();
          }
        })
      }
    })
  }
}

复制代码

这里需要着重说明一下validateSubmit指令,这个指令绑定到提交按钮上,在点击的时候执行校验,校验通过之后执行提交操作。但是这里的实现方式不是特别友好:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

1.需要获取当前组件中的所有input元素,给他们绑定并执行blur事件,以此来执行validateParams指令中的校验逻辑。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

2.需要获取当前组件中的所有错误提示元素,如果他们存在就不能执行提交操作。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

3.当组件内不含任何错误提示元素时,就表示校验通过,执行当前组件内的submit函数,所以每个表单组件的提交函数都只能命名为submit文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

然后我们再看下指令validateParams,该指令需要绑定到表单input元素上,并把校验规则当作参数写入。当该input元素失焦时,会执行指令中给当前元素绑定的事件中的逻辑。这些逻辑分为三个步骤,我已经写在注释里了,现在我们来看下具体实现。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

  • 重置所有错误提示
/**
 * 重置当前节点样式
 * @param el: HTMLElement,传入当前绑定的input元素
 */
const resetError = (el: HTMLElement) => {
  el.className = el.className.replace('input-error', '').trim();
  if ( el.parentNode ) {
    const ErrorNode = el.parentNode.querySelector('.error-tips');
    if (ErrorNode) {
      el.parentNode.removeChild(ErrorNode);
    }
  }
};
复制代码
  • 获取自定义指令中传入的校验规则参数和表单输入的值
// binding.value是传入自定义指令的参数,以数组的形式
for (const rule of binding.value) {
  // 分别获取到自己定义的校验规则并执行
  const { min, max, message, required, pattern } = rule;
  if ( min && InputEl.value.length < min ) {
    // 如果不符合校验,执行报错函数
    validateError(InputEl, message);
    break;
  }
  if ( max && InputEl.value.length > max ) {
    validateError(InputEl, message);
    break;
  }
  if ( !!required && !InputEl.value ) {
    validateError(InputEl, message);
    break;
  }
  if ( pattern && !pattern.test(InputEl.value) ) {
    validateError(InputEl, message);
    break;
  }
  if ( rule && typeof rule === 'function' ) {
    rule(vNode.context, InputEl.value, validateError, InputEl);
    break;
  }
}
复制代码
  • 校验不符合,执行报错函数
/**
 * 执行错误提示函数,用input-error 类名和含有错误信息的p元素表示未通过校验
 * @param el: HTMLElement,传入当前绑定的input元素
 * @param errorMsg: string,传入错误提示信息
 */
const validateError = (el: HTMLElement, errorMsg: string) => {
  if (Array.prototype.includes.call(el.classList, 'input-error')) {
    //如果当前组件里已经有了错误提示信息,什么也不做
    return;
  } else {
    const errorNode = document.createElement('p');
    errorNode.className = 'error-tips';
    errorNode.textContent = errorMsg;
    if (el.parentNode) {
      // 在当前input 元素后追加一个p元素,内容为错误提示
      el.parentNode.appendChild(errorNode);
    }
    // 在当前input 元素上添加一个input-error类名
    el.className += ' input-error';
  }
};
复制代码

现在我就把自己实现的这个表单校验插件大致说完了,下面我们看下具体使用。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

3.自定义校验指令v-validateParams使用

首先新建校验规则文件:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

// rules.ts
export const required = (message) => ({
  message,
  required: true
});
export const min = (message, length=3) => ({
  message,
  min: length
})
export const max = (message, length=15) => ({
  message,
  max: length
})
export const pattern = (message, reg) => ({
  message,
  pattern: reg
})

// form.vue
<template>
  <div>
    <div class="form-item">
      <label for="userEmail">用户名:</label>
      <input id="userEmail" class='v-check' type="text" v-model="userName"
        v-validateParams="[inputNameRequired, inputNameMin, inputNameMax, inputNamePattern]">
    </div>
    <button class="btn" v-if="show" type="success" v-checkSubmit>确认</button>
  </div>
</template>
<script lang='ts'>
import { Component, Vue, Prop } from 'vue-property-decorator';
import { max, min, required, name, pattern} from 'rules';

@Component({
  components: {},
})
export default class Auth extends Vue {
  private show: boolean = true;
  private userName: string = '';
  private inputNameMax = max('请不要超过20个字符');
  private inputNameMin = min('请不要小于3个字符');
  private inputNameRequired = required('请输入用户名');
  private inputNamePattern  = pattern('请输入符合要求的用户名', /^[a-zA-Z0-9_-]{4,16}$/);
  private submit() {
    alert('通过校验');
  }
}
</script>
复制代码

通过这个例子我们可以看到,使用时需要将校验规则引入并赋给vue实例中的数据。然后在模板中,需要给input标签添加v-check类名,再使用v-validateParams指令,并传入参数。提交按钮需要调用v-checkSubmit指令。按照这种方式就能够使用自己开发的这个表单校验插件。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

4. 当前方式存在的问题

虽然表单校验可以使用了,但是存在一些显而易见的问题:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

1.js和html耦合度较高,插件还需要获取dom元素,组件的html模板中还需要添加指定的类名。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

2.在vue中使用dom操作,不符合vue的设计思路,实现方式也不优雅。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

3.校验规则的校验逻辑在指令定义时写定了,添加或删除都需要改动插件代码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

4.提交指令根据当前组件内的是否含有特定dom来判断当前校验状态,且执行提交的函数名称也在指令逻辑中写定了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

我根据现有一个demo结合着自己的需求来实现的这个表单校验插件,开发的过程中我已经知道这么写问题很多,甚至不能称之为一个合格的插件。同时也清楚的认识到自己的javascript水平还很初级,需要很大进步。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

当前开发的表单插件的主要问题在于如何将插件中的校验状态返回到组件内。我们可以在插件内维护一个事件处理函数,将校验规则传入并校验,再将校验结果直接传给组件内。这样就可以避免大量的dom操作。之后我需要尽快对这个插件进行更科学合理的重构。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

作者:genetalks_大数据
链接:https://juejin.im/post/5ce6432f6fb9a07ef63fb5fe
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html

文章源自菜鸟学院-https://www.cainiaoxueyuan.com/gcs/12805.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/gcs/12805.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定