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 这样的内置指令来声明式地使用模板,这样更高效,也对服务端渲染更友好。

相关推荐
一丝晨光23 分钟前
C++、Ruby和JavaScript
java·开发语言·javascript·c++·python·c·ruby
Front思24 分钟前
vue使用高德地图
javascript·vue.js·ecmascript
zqx_71 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己1 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称2 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色2 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript
NiNg_1_2342 小时前
npm、yarn、pnpm之间的区别
前端·npm·node.js
秋殇与星河2 小时前
CSS总结
前端·css
NiNg_1_2342 小时前
Vue3 Pinia持久化存储
开发语言·javascript·ecmascript
读心悦2 小时前
如何在 Axios 中封装事件中心EventEmitter
javascript·http