Vue3 自定义指令Custom Directives

简介

在vue中重用代码的方式有:组件、组合式函数。组件是主要的构建模块,而组合式函数更偏重于有状态的逻辑。

指令系统给我们提供了例如:v-model、v-bind,vue系统允许我们自定义指令,自定义指令也是一种重用代码的方式。自定义指令常用于封装一些普通元素的Dom底层访问逻辑。

定义

一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数。

自定义指令的钩子函数如下:

javascript 复制代码
const myDirective = {
  // 在绑定元素的 attribute 前
  // 或事件监听器应用前调用
  created(el, binding, vnode, prevVnode) {
    // 下面会介绍各个参数的细节
  },
  // 在元素被插入到 DOM 前调用
  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) {}
}

使用

下面这两个使用的例子都源自网络,在原有的基础上加入了一些改动

一个小栗子,使用全局自定义指令封装防抖代码段

javascript 复制代码
export const longpress = {
  created(el, binding, vNode) {
    // console.log("long press event 1");
    if (typeof binding.value !== 'function') {
      throw 'callback must be a function'
    }
    // 定义变量
    let pressTimer = null
    // 创建计时器( 2秒后执行函数 )
    let start = (e) => {
      if (e.type === 'click' && e.button !== 0) {
        return
      }
      if (pressTimer === null) {
        pressTimer = setTimeout(() => {
          handler()
        }, 2000)
      }
    }
    // 取消计时器
    let cancel = (e) => {
      if (pressTimer !== null) {
        clearTimeout(pressTimer)
        pressTimer = null
      }
    }
    // 运行函数
    const handler = (e) => {
      binding.value(e)
    }
    // 添加事件监听器
    el.addEventListener('mousedown', start)
    el.addEventListener('touchstart', start)
    // 取消计时器
    el.addEventListener('click', cancel)
    el.addEventListener('mouseout', cancel)
    el.addEventListener('touchend', cancel)
    el.addEventListener('touchcancel', cancel)
  },
  // 当传进来的值更新的时候触发
  updated(el, { value }) {
    el.$value = value
  },
  // 指令与元素解绑的时候,移除事件绑定
  unmounted(el) {
    el.removeEventListener('click', el.handler)
  },
}
javascript 复制代码
import App from './App.vue'
import { preventReClick } from './utils/directivebox';

const app = createApp(App)

// 全局指令的挂载
preventReClick(app);
javascript 复制代码
<template>
  <div class="box">
    <!-- 这里必须使用el-button,因为这个防抖的功能是通过控制elementPlus中的disable属性实现的 -->
    <el-button class="btn" v-preventReClick @click="buttonEvent">使用全局自定义指令,做防抖的处理</el-button>
  </div>
</template>
<script setup>

let clickNum = 0

// 使用全局自定义指令处理防抖
/*
 运行后可以看到,在连续多次点击button时,3000ms后才会打印一次这个buttonEvent中的log
 因为在全局自定义指令中拦截了click的事件,在3000ms内组件被设置为disabled的状态
 */
function buttonEvent() {
  clickNum++
  console.log("clickNum = ", clickNum); 
}

</script>
<style scoped lang="less">
.box {
  display: flex;
  flex-direction: column;
  .btn {
    padding: 20px 40px 20px 40px;
    background-color: aquamarine;
    margin-bottom: 30px;
  }
}
</style>

一个小栗子,使用局部自定义指令封装长按元素两秒的点击事件:

javascript 复制代码
export const longpress = {
  created(el, binding, vNode) {
    // console.log("long press event 1");
    if (typeof binding.value !== 'function') {
      throw 'callback must be a function'
    }
    // 定义变量
    let pressTimer = null
    // 创建计时器( 2秒后执行函数 )
    let start = (e) => {
      if (e.type === 'click' && e.button !== 0) {
        return
      }
      if (pressTimer === null) {
        pressTimer = setTimeout(() => {
          handler()
        }, 2000)
      }
    }
    // 取消计时器
    let cancel = (e) => {
      if (pressTimer !== null) {
        clearTimeout(pressTimer)
        pressTimer = null
      }
    }
    // 运行函数
    const handler = (e) => {
      binding.value(e)
    }
    // 添加事件监听器
    el.addEventListener('mousedown', start)
    el.addEventListener('touchstart', start)
    // 取消计时器
    el.addEventListener('click', cancel)
    el.addEventListener('mouseout', cancel)
    el.addEventListener('touchend', cancel)
    el.addEventListener('touchcancel', cancel)
  },
  // 当传进来的值更新的时候触发
  updated(el, { value }) {
    el.$value = value
  },
  // 指令与元素解绑的时候,移除事件绑定
  unmounted(el) {
    el.removeEventListener('click', el.handler)
  },
}
javascript 复制代码
<template>
  <div class="box">
    <el-button class="btn" v-long-press="longPressEvent">使用全局自定义指令,实现长按处理事件</el-button>
  </div>
</template>
<script setup>
import { longpress } from './utils/derective.js'

// 在setup语法糖中使用v打头驼峰命名的方式声明一个局部的自定义指令
const vLongPress = longpress;

function longPressEvent(){
  console.log("button的长按点击事件");
}

</script>
<style scoped lang="less">
.box {
  display: flex;
  flex-direction: column;
  .btn {
    padding: 20px 40px 20px 40px;
    background-color: aquamarine;
    margin-bottom: 30px;
  }
}
</style>

小结

注意:只有当所需功能只能通过直接的 DOM 操作来实现时,才应该使用自定义指令。其他情况下应该尽可能地使用 v-bind 这样的内置指令来声明式地使用模板,这样更高效,也对服务端渲染更友好。

相关推荐
学代码的小前端1 分钟前
0基础学前端-----CSS DAY9
前端·css
joan_855 分钟前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui
还是大剑师兰特28 分钟前
什么是尾调用,使用尾调用有什么好处?
javascript·大剑师·尾调用
m0_7482361136 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_748248941 小时前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_748235611 小时前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O3 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink6 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者8 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart