當我們在業務中碰到痛點問題的時候,會導致部分程式碼邏輯不清晰。這時就需要用皮鞭、蠟燭去解決它、調教它,將它的邏輯變得清晰起來。
概述:上篇文章介紹了,如何函式式呼叫表單元件從而減少維護其狀態的方法基於Vue構造器建立Form元件的通用解決方案。現在來介紹下如何處理表單驗證問題,在大多數與後端通訊的場景中,表單驗證是一個不可避免的問題。它承載了許多的邏輯以及狀態,在某些過於複雜的場景中,會使得導致程式碼極其臃腫。校驗的規則與狀態的處理耦合在一起,導致後續難以接手和開發。
1. 前言
現有的Vue表單驗證外掛有vuelidate
、vee-validate
等,但是如果場景簡單的話,沒有引入外掛的必要,但是場景複雜的話,驗證規則又要定製化的時候,應用起來沒那麼順手。就像初次調教的人,就不能上皮鞭。所以,如何調教一個屬於自己的,聽話的Vue表單驗證外掛,是十分有必要的。
2. 例項以及使用規則
例項程式碼:GitHub - FatGe/fat-validator: fat-validator-demo
原始碼:GitHub - FatGe/fat-validator: fat-validator
程式碼中,有
<template>
<input
placeholder="請輸入"
v-model="form.nativeInput"
v-validate:nativeInput="validates.nativeInput"
/>
<span class="u-info">{{ errors.get('nativeInput').warn }}</span>
</template>
<script>
import { validateResult } from 'fat-validator'
export default {
mixins: [ validateResult ],
data () {
return {
form: {
nativeInput: ''
},
validates: {
nativeInput: [{
need: () => !!this.form.nativeInput,
warn: '不能為空'
}]
}
}
}
}
</script>
複製程式碼
上述程式碼中,v-validate:nativeInput.blur="validates.nativeInput"
自定義指令中
-
arg
(nativeInput)代表著校驗的結果的key; -
modifiers
(blur)代表著失焦校驗; -
value
(validates.nativeInput)代表著data
中維護的具體的校驗規則。
當input
元素失焦時,會自動進行校驗,將校驗結果errors.get('nativeInput').warn
顯示在頁面中。
3. 原理
上述自定義指令的主要原理如下圖
也就是元件通過自定義指令v-*
,將資料以及驗證規則傳遞給處理Handler,Handler將結果返回給元件,具體如下:
-
元件通過自定義指令將資料以及校驗規則傳遞給Handler:
從例項中的
v-validate:nativeInput.blur
可以看出,選用了自定義指令作為連線被校驗元件和校驗規則之間的橋樑,主要是它兩個特點:- 鉤子函式:Vue自定義指令,存在著
bind
、inserted
、update
等鉤子函式,其中bind
、unbind
與元素的display息息相關,這樣滿足了表單掛載、登出的語義,且只觸發一次; - 建構函式提供了大量的引數,可以方便與元件通訊,如
el
、bind
等。
我們利用指令可以傳遞的值
arg
、modeifiers
、value
,傳遞引數,其中為了方便校驗規則與資料的結合,我們利用箭頭函式將校驗函式繫結在當前context內,如下程式碼nativeInput: [{ // need function context = dangqian zujian need: () => !!this.form.nativeInput, warn: '不能為空' }] 複製程式碼
將上述資料傳遞給Handler,並且為了控制校驗的先後順序,將驗證規則定義為Array,這樣就會只獲取第一個報錯資訊;
- 鉤子函式:Vue自定義指令,存在著
-
解析校驗指令,並將資料傳遞至Handler:
該自定義指令的註冊以及解析如下
Vue.directive('validate', { bind (element, binding, vnode) { // 傳給引數arg => 作為校驗結果中的key // 指令繫結值value => 作為校驗規則 // 修飾符modifiers => 作為出發校驗的event型別 const { arg: name, modifiers, value: rules } = binding const method = Object.keys(modifiers)[0] }, unbind (element, binding) { // 註冊 const { arg: name, modifiers } = binding const method = Object.keys(modifiers)[0] } }) 複製程式碼
上述code解釋了,如何將rules傳入指令中,接下來介紹如何處理這些規則,當完成元件上
v-validate
的解析時,就會在eventHandler中訂閱一個校驗物件。export default function (Vue) { Vue.directive('validate', { bind (element, binding, vnode) { //... pre code // 獲取當前元件的context context = vnode.context // 將eventHandler$$1繫結在當前Form的context // 防止動態切換時無法與元件通訊 if (eventHandler$$1) { eventHandler$$1.bind(context) } else { eventHandler$$1 = new eventHandler(context) } // 如果method存在的話,當event出發時的處理函式 const handler = function () { eventHandler$$1.broadcast(name) } // 在hanler中訂閱一個規則處理物件 eventHandler$$1.subscribe({ name, method, rules, element, handler }) method && on(element, method, handler) }, unbind (element, binding) { //... pre code // 當Form登出時,垃圾處理 const handler = eventHandler$$1.removeSubscribe(name) method && off(element, method, handler) } }) } 複製程式碼
-
eventHandler
內維護了subscribers
用於儲存rule function
以及相應的warn info
:export default class eventHandler { // 建構函式 constructor (context) { this.context = context this.subscribers = {} } // 動態切換 bind (context) { this.context = context } // 訂閱,將其維護在eventHandler的subscribers內 subscribe (options) { const { name } = options this.subscribers[name] = Object.assign({}, options) } // 當校驗事件觸發時,進行校驗 broadcast (name) { const { context, subscribers } = this const { rules } = subscribers[name] // 校驗結果 const error = findFailRule(rules) context.$forceUpdate() return error.success } } 複製程式碼
當需要校驗時就會觸發
broadcast
,獲取相關的rule fucntion
來校驗表單,核心是findFailRule function
,遍歷rules獲取不符合的規則。由於我們將規則定義為Array,只需要find
不符合的規則即可,具體程式碼如下findFailRule = (value = {}, rules) => { let failRule = null if (Array.isArray(rules)) { failRule = rules.find(item => { return !item.need(value) }) } return { warn: failRule ? failRule.warn : '', success: !failRule } } 複製程式碼
最後維護一個
validateResult
物件,通過mixins
注入到Form元件內,將校驗結果傳遞給Form元件,其具體定義如下const validateResult = { data () { return { errors: { get (param) { return this[param] ? this[param] : { warn: '', success: true } } } } } } 複製程式碼
在之前,我們將
eventHandler
繫結在當前context
內,這樣可以獲取到注入的errors
狀態,當校驗完成時,可將報錯資訊更新至error
中,再利用context.$forceUpdate()
進行更新。
以上是一個簡單的Vue表單驗證外掛的雛形,為了方便開發業務,還需它具備以下API:
validate
: 驗證指定規則;validateAll
: 驗證所有規則 => 通常用於判斷Form元件是否可以可提交;reset
: 重置驗證結果 => 例如input一般要求聚焦重置;resetAll
: 重置所有驗證結果。 為了方便引入,利用Vue.use()
將上述自定義指令封裝程Vue的外掛。
4. 結論
基於上述外掛,可以自定義函式規則,只需要滿足return
一個Boolean值即可。將業務中,常用的驗證規則進行整理、封裝,最後調教出一個聽話的驗證外掛。
PS:有兩點坑:
- 由於全域性維護了處理中心,所以同一個Form內,name不能相同;
- 須將驗證函式繫結在data內部,所以不能使用
data: () => ({})
原創宣告: 該文章為原創文章,轉載請註明出處。