使用vue自定義指令開發一個表單驗證插件validate.ts(帶更新)

這段時間在進行一個新項目的前期搭建,新項目框架採用vue-cli3和typescirpt搭建。由於項目比較輕量,因此基本沒有使用額外的ui組件,有時候咱們須要的一些基礎組件我就直接本身開發了。今天就來介紹一下如何利用vue的自定義指令directive來開發一個表單驗證插件的過程。本文是以ts爲基礎開發的,若是同窗們須要js版的只須要把ts語法轉爲js便可。javascript

1.vue插件開發

關於vue的插件開發,官方文檔裏有很清晰的說明,詳情能夠去閱讀開發文檔。此次開發的表單驗證插件validate.ts就是利用了這種方式。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編譯生成的虛擬節點。前端

咱們經過註冊一個全局指令v-validateParams指令,綁定到輸入表單的input標籤上來校驗當前輸入值是否符合要求。vue

2.v-validateParams指令

最開始我參考了網上的一些代碼,思路以下:java

  • 總體思路
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指令,這個指令綁定到提交按鈕上,在點擊的時候執行校驗,校驗經過以後執行提交操做。可是這裏的實現方式不是特別友好:node

  1. 須要獲取當前組件中的全部input元素,給他們綁定並執行blur事件,以此來執行validateParams指令中的校驗邏輯。
  2. 獲取當前組件中的全部錯誤提示元素,若是他們存在就表示沒有經過校驗,不能繼續執行後面的邏輯。
  3. 當組件內不含任何錯誤提示元素時,表示校驗經過,執行當前組件內的submit函數,因此每一個表單組件的提交函數都只能命名爲submit

咱們再看下指令validateParams的內部實現。該指令須要綁定到表單input元素上,並把校驗規則看成參數傳入。當該input元素失焦時,會執行指令中給當前元素綁定的事件中的邏輯。這些邏輯分爲三個步驟,我已經寫在註釋裏了,如今咱們來看下具體實現。vue-cli

  • 重置全部錯誤提示
/** * 重置當前節點樣式 * @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 ( !!required && !InputEl.value ) {
    // 若是不符合校驗,執行報錯函數
    validateError(InputEl, message);
    break;
  }
  if ( min && InputEl.value.length < min ) {
    validateError(InputEl, message);
    break;
  }
  if ( max && InputEl.value.length > max ) {
    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';
  }
};
複製代碼

如今我就把本身實現的這個表單校驗插件大體說完了,下面咱們看下具體使用。segmentfault

3.表單插件的使用

首先新建校驗規則文件:數組

// 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" 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({})
export default class Auth extends Vue {
  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指令。按照這種方式就可以使用本身開發的這個表單校驗插件。前端工程師

3. 當前方式存在的問題

雖然表單校驗可使用了,可是存在一些顯而易見的問題:

  1. js和html耦合度較高,插件還須要獲取dom元素,組件的html模板中還須要添加制定類名。
  2. 在vue中使用dom操做,不符合vue的設計思路,實現方式也不優雅。
  3. 校驗規則的校驗邏輯在指令定義時寫定了,添加或刪除都須要改動插件代碼。
  4. 提交指令根據當前組件內的是否含有特定dom來判斷當前校驗狀態,且執行提交的函數名稱也在指令邏輯中寫定了。

我根據現有一個demo結合着本身的需求來實現的這個表單校驗插件,開發的過程當中我已經知道這麼寫問題不少,同時也清楚的認識到本身的javascript水平還很初級,須要繼續努力學習。

當前開發的表單插件的主要問題在於如何將插件中的校驗狀態返回到組件內。咱們能夠在插件內維護一個事件處理函數,將校驗規則傳入並校驗,再將校驗結果直接傳給組件內。這樣就能夠避免大量的dom操做。以後我須要儘快對這個插件進行更科學合理的重構。

後續更新:對錶單插件進行優化

參考文章

vue插件

vue自定義指令

vue使用自定義指令實現表單校驗

重構:從 0.1 構建一個 Vue 表單驗證插件

va.js——Vue 表單驗證插件的寫做過程


做者簡介: 宮晨光,人和將來大數據前端工程師。

相關文章
相關標籤/搜索