vue3 自定义指令

除了Vue内置的一些指令外,我们还可以注册一些自定义指令,以方便我们做一些重复性的DOM操作
但是需要注意的是,只有当某一功能只能通过直接操作DOM来实现时,我们使用自定义指令,其他情况下尽可以使用 v-bind 这类的内置指令实现,这样会更高效。

1、自定义指令钩子

vue3 中重新定义了自定义指令的生命周期钩子,与组件的生命周期钩子函数类似

  • created:在绑定元素的 attribute 前或事件监听器应用前调用
  • beforeMount:第一次绑定到元素且被插入到 DOM 前调用
  • mounted:在绑定元素的父组件及他自己的所有子节点都挂载完成后调用,大部分自定义指令都使用这个钩子来编写指令实现逻辑
  • beforeUpdate:绑定元素的父组件更新前调用
  • updated:在绑定元素的父组件及他自己的所有子节点都更新后调用
  • beforeUnmount:绑定元素的父组件卸载前调用
  • unmounted:绑定元素的父组件卸载后调用

2、自定义指令参数

每个指令的钩子函数的参数都是一致的,以下以mounted为例:

mounted(el, binding, vnode, prevVnode) {
    // 书写自己的逻辑代码
  },
  • el:指令绑定到的元素。这可以用于直接操作 DOM
  • binding:对象,包含以下属性:
    • value:传递给指令的值。例如在 v-my-directive=“1 + 1” 中,值是 2
    • oldValue:之前的值,仅在 beforeUpdate 和 updated 中可用。无论值是否更改,它都可用
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 “foo”
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例
    • dir:指令的定义对象
  • vnode:代表绑定元素的底层 VNode
  • prevVnode:代表之前的渲染中指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用

3、自定义指令实现方式:ref

<input type="text" ref="inputRef" />

const inputRef = ref(null)
inputRef.value.focus()

4、自定义指令实现方式:局部注册

组件内部可以通过使用 directives 选项创建自定义指令,但只能在当前组件中使用

<input type="text" v-focus />

import { Directive } from 'vue'
const vFocus: Directive = {
	mounted(el, binding, vnode, prevVnode) {
    	el.focus()
  	},
}

5、自定义指令实现方式:全局注册

5.1、 main.js 中可以使用

在 main.js 中可以使用 app 的 directives 方式创建自定义指令,此时可以在任意组件中被使用

app.directive('focus', {
	mounted(el, binding, vnode, prevVnode) {
    	el.focus()
  	},
})

5.2、directive文件夹

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

import { App } from 'vue'
import drag from './drag'

const directives: any = {
  drag,
}

export default (app: App<Element>) => {
  Object.keys(directives).forEach((key: any) => {
    app.directive(key, directives[key])
  })
}

drag.ts

import { Directive, DirectiveBinding } from 'vue'

const drag: Directive<any, void> = (
  el: HTMLElement,
  bingding: DirectiveBinding
) => {
  // console.log('el', el) // 这个 el 就是挂载 v-drag 指定的元素
  const right = window.innerWidth - el.clientWidth
  const bottom = window.innerHeight - el.clientHeight

  // 获取内部第一个div元素
  let moveElement: HTMLDivElement = el.firstElementChild as HTMLDivElement
  moveElement.style.cursor = 'move'
  // 鼠标按下的事件
  const mouseDown = (e: MouseEvent) => {
    let X = e.clientX - el.offsetLeft
    let Y = e.clientY - el.offsetTop

    // 鼠标移动事件
    const move = (e: MouseEvent) => {
      // 控制向左移动最左不能超出窗口左侧 且 向右移动最右不能让盒子超出窗口右侧
      let moveX =
        el.offsetLeft < 0 ? 0 : e.clientX - X > right ? right : e.clientX - X
      // 控制向上移动最上不能超出窗口顶部 且 向下移动最下不能让盒子超出窗口底部
      let moveY =
        e.clientY - Y < 0 ? 0 : e.clientY - Y > bottom ? bottom : e.clientY - Y

      el.style.left = moveX + 'px'
      el.style.top = moveY + 'px'
    }
    document.addEventListener('mousemove', move)
    // 在鼠标抬起时去移除移动事件
    document.addEventListener('mouseup', () => {
      // 移除移动事件
      document.removeEventListener('mousemove', move)
    })
  }
  // 为获取到的div盒子绑定鼠标按下事件
  moveElement.addEventListener('mousedown', mouseDown)
}

export default drag

不推荐在组件上使用自定义指令,当组件具有多个根节点时指令将会被忽略且抛出一个警告
当在组件上使用自定义指令时,它会始终应用于组件的根节点,类似于 attributes,但是不同的是,指令不能通过 v-bind=“$attrs” 来传递给一个不同的元素

6、自定义指令书写方式

6.1、完整式

const myDirective = {
	created(el, binding, vnode, prevVnode) {},

	beforeMount(el, binding, vnode, prevVnode) {},

	mounted(el, binding, vnode, prevVnode) {},

	beforeUpdate(el, binding, vnode, prevVnode) {},

	updated(el, binding, vnode, prevVnode) {},

	beforeUnmount(el, binding, vnode, prevVnode) {},

	unmounted(el, binding, vnode, prevVnode) {}
}

6.2、简化形式:函数式

<input type="text" v-focus />

import { Directive } from 'vue'
const vFocus: Directive = (el, binding, vnode, prevVnode) => {
	el.focus()
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值