vue-自定义指令+hook

avatar

一个异步校验表单内容的自定义指令+hook

开头

前段时间遇到需要给表单rules添加异步校验敏感词需求,遂记录下实现过程

directive

先放上vue官方文档对自定义指令地址: 地址

可以看到 bindupdate 生命周期钩子为这次功能所要用到的,在vnode绑定当前指令及更新时
在官方文档描述中,指令生命钩子的第三个参数也就是:

  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情

vnode

结合源码可以理解,vnode是vue的虚拟dom,可以理解为一个个装有vue组件配置的对象,经过一系列数据劫持及模板编译渲染等复杂过程,最终生成到页面上。
以及了解vnode的挂载过程及相关props更新过程等,可以借助相关hook

props 数据变化一定会触发父组件的重新渲染,而远程请求接口后,拿到结果可以手动触发当前表单中的rules规则进行改变,从而达到每次请求接口后都能渲染出错误信息及交互保持一直。并且在业务代码中无需写大量rules

直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import { fetchxxx } from '@/api'

/**
* @description
* 敏感信息指令原理是通过给绑定的vnode添加多一条rule
*/

const sensitiveFunction = (function () {
return (el, binding, vnode, oldVnode) => {
const fieldVnode = vnode.child
const notSensitive = binding?.value?.notSensitive
if (notSensitive) return
const fieldTitle = binding?.value?.fieldName || fieldVnode.label || ''
const fieldRules = vnode.componentInstance.rules
if (fieldRules && fieldRules.some((i) => i.type === 'sensitive')) return
const callback = binding?.value?.callback
let sensitiveResultMessage = ''
const validator = {
type: 'sensitive',
trigger: 'onSubmit',
message: (value, rule) => {
if (callback && typeof callback === 'function') return ''
return `${fieldTitle}含有敏感信息"${sensitiveResultMessage}"请修改`
},
validator: async function (value) {
try {
if (!value) return Promise.resolve(true)
const sensitiveResult = await fetchxxx(value)
if (!sensitiveResult) return Promise.resolve(true)
if (sensitiveResult) {
if (callback && typeof callback === 'function') {
sensitiveResultMessage = ''
callback(sensitiveResult)
return Promise.resolve(false)
}
sensitiveResultMessage = sensitiveResult
return Promise.resolve(false)
}
} catch (error) {
console.log({ error })
}
}
}
// 允许vnode不存在rules的props
if (
!vnode.componentInstance.rules ||
!vnode.componentInstance.rules.length
) {
vnode.componentOptions.propsData.rules = [validator]
vnode.data.hook.prepatch(vnode, vnode)
} else {
vnode.componentInstance.rules.push(validator)
}
}
})()

export default {
bind (el, binding, vnode, oldVnode) {
return sensitiveFunction(el, binding, vnode, oldVnode)
},
update (el, binding, vnode, oldVnode) {
return sensitiveFunction(el, binding, vnode, oldVnode)
}
}

结语

关键代码其实就在

1
2
vnode.componentOptions.propsData.rules = [validator]
vnode.data.hook.prepatch(vnode, vnode)

可以借组自定义指令中传递参数实现更复杂功能