vue3中的自定义指令

发布时间 2023-04-10 14:39:00作者: 月下云生

1.适用到自定义指令的场景 防抖、图片懒加载、一键 Copy的功能、拖拽、页面水印、权限校验、输入框自动聚焦、相对时间转换、下拉菜单

2.个人需求:在后台系统中,有很多表单提交组件,其中很多限制数字且限制条件不同。最初使用

oninput="value=value.replace(/[^\d]/g,'')"

来显示只允许输入数字,后面发现bug,如果输入汉字,再输入数字,在弹窗中是无法读取该输入框内容的,如果再加入一些条件限制则会失去其便利性,有人说可以rule结合validate来实现,简单的的确可以。但是如果是数组对象里的数组对象里的某个值要校验呢?

功能的使用行吧。因此考虑替代自定义指令方案:

在src文件夹新建一个directive文件夹,存放自定义指令文件。新建index.ts将以后的所有的自定义指令自动导出,index.ts

import { App } from 'vue'

const modules = import.meta.glob('../directive/**/**.ts')
// 自动导入当前文件夹下的所有自定义指令(默认导出项)
export default (app:App<Element>):void => {
    for (const path in modules) {
        // 排除当前文件
        if(path !== '../directive/index.ts') {
            modules[path]().then((mod) => {
                mod.default(app)
            })
        }
    }
}

在main.ts中使用:

const app = createApp(App)
direct(app)

新建num.ts文件自定义指令:

import { App } from 'vue'
export default (app: App<Element>): void => {
    app.directive('num', {
        created: (el, binding, vnode, prevNode) => {
            console.log("第一个参数:当前绑定的DOM 元素", el);
            console.log("---------------------------------------------");
            console.log("第二个参数:是个对象", binding);
            //使用指令的组件实例
            console.log("binding.instance:第一个参数", binding.instance);
            //传递给指令的值。
            console.log("binding.value:第二个参数", binding.value);
            //先前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否有更改都可用。
            console.log("binding.oldValue:第三个参数", binding.oldValue);
            //传递给指令的参数(如果有的话)。例如在 v-my-directive:foo 中,arg 为 "foo"。
            console.log("binding.arg:第四个参数", binding.arg);
            //包含修饰符(如果有的话) 的对象。
            console.log("binding.modifiers:第五个参数", binding.modifiers);
            //一个对象,在注册指令时作为参数传递。
            console.log("binding.dir:第六个参数", binding.dir);
            console.log("---------------------------------------------");
            console.log("第三个参数:当前元素的虚拟DOM 也就是Vnode", vnode);
            console.log("---------------------------------------------");
            console.log("第四个参数:prevNode 上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用 ", prevNode);
            // console.log("初始化====>");
        },
        beforeMount(...args: Array<any>) {
            // 在元素上做些操作
            // console.log(args);
            // console.log("初始化一次=======>");
        },
        mounted() {
            // console.log("初始化========>");
        },
        beforeUpdate() {
            // console.log("更新q前");
        },
        updated() {
            // console.log("更新结束");
        },
        beforeUnmount() {
            // console.log("======>卸载之前");
        },
        unmounted() {
            // console.log("======>卸载完成");
        }
    })
}

二. 钩子函数以及生命周期钩子参数详解
  1. 钩子函数

  •  created 元素初始化的时候
  •  beforeMount 指令绑定到元素后调用 只调用一次
  •  mounted 元素插入父级dom调用
  •  beforeUpdate 元素被更新之前调用
  •  update 这个周期方法被移除 改用updated
  • beforeUnmount 在元素被移除前调用
  • unmounted 指令被移除后调用 只调用一次

  2. 生命周期钩子参数详解(每个钩子都有参数)

  • 第一个 el 当前绑定的DOM 元素
  • 第二个 binding

  ■ instance:使用指令的组件实例。
  ■ value:传递给指令的值。例如,在 v-my-directive=“1 + 1” 中,该值为 2。
  ■ oldValue:先前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否有更改都可用。
  ■ arg:传递给指令的参数(如果有的话)。例如在 v-my-directive:foo 中,arg 为 “foo”。
  ■ modifiers:包含修饰符(如果有的话) 的对象。例如在 v-my-directive.foo.bar 中,修饰符对象为 {foo: true,bar: true}。
  ■ dir:一个对象,在注册指令时作为参数传递。例如,在以下指令中

  • 第三个 当前元素的虚拟DOM 也就是Vnode
  • 第四个 prevNode 上一个虚拟节点,仅在 beforeUpdate 和 updated 钩子中可用

修改以后:

 import { App } from 'vue'
 export default (app: App<Element>): void => {
    app.directive('nums', {
        mounted(el) {
            console.log(el)
            const reg = /^\+?[1-9][0-9]*$/
            el.addEventListener('input', (event: any) => {
                console.log(event.target.value)
                if (!reg.test(event.target.value)) {
                    event.target.value = ''
                }
            })
        }
    })
 }

测试,发现同样读取不到值,发现自定义事件以后把原有的触发事件阻止了。再次修改,手动触发:

export default (app: App<Element>): void => {
    app.directive('num', {
        mounted(el, binding, vnode: any) {
            console.log(vnode)
            const reg = /^\+?[1-9][0-9]*$/
            el.addEventListener('input', (event: any) => {
                // console.log(event.target.value)
                if (!reg.test(event.target.value)) {
                    event.target.value = ''
                }
                vnode.ctx.emit('update:modelValue', event.target.value)
            })
            el.addEventListener('change', (event: any) => {
                el.dispatchEvent(new Event('change ', { bubbles: true }))
                vnode.ctx.emit('update:modelValue', event.target.value)
            })
            el.addEventListener('clear', (event: any) => {
                el.dispatchEvent(new Event('clear ', { bubbles: true }))
                vnode.ctx.emit('update:modelValue', event.target.value)
            })
            el.addEventListener('focus', (event: any) => {
                el.dispatchEvent(new Event('focus ', { bubbles: true }))
                vnode.ctx.emit('update:modelValue', event.target.value)
            })
            el.addEventListener('blur', (event: any) => {
                el.dispatchEvent(new Event('blur ', { bubbles: true }))
                vnode.ctx.emit('update:modelValue', event.target.value)
            })
        }
    })
}

测试,可以了:最后加上一些提示,代码地址如下:

 https://gitee.com/yuexiayunsheng/vue3learn/blob/master/src/views/CustomInstruct.vue

参考:https://blog.csdn.net/MM_520131400/article/details/123931645