iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >如何在Vue3中实现自定义指令(超详细!)
  • 963
分享到

如何在Vue3中实现自定义指令(超详细!)

2024-04-02 19:04:59 963人浏览 八月长安
摘要

目录前言生命周期钩子的参数简化形式对象字面量在组件上使用指令几个实用的自定义指令自动聚焦v-focus防抖v-debounce节流v-throttle弹窗隐藏v-hide总结在开发V

开发Vue项目时,大多数人都会使用到Vue内置的一些指令,例如v-model、v-if等,在使用的时候不知道有没有想过自己也来实现一个指令呢。本文就以vue3项目为基础,从原理、方法到实际案例、注意事项,尽可能细致的讲解如何自定义指令。

前言

我们需要明白为什么需要自定义一个指令,其实就是想更加简洁地重复使用操作DOM的逻辑,这就和组件化和组合式函数差不多。

不管是Vue内置的指令还是自定义的指令,都有类似于组件的生命周期,我们可以在不同的生命周期完成不同的逻辑操作,并绑定到组件元素上,这样就产生了自定义指令。在Vue3中,我们有三种方式可以定义指令:

  • 如果是在<script setup>定义组件内的指令,有一个语法糖可以使用:任何以v开头的驼峰式命名的变量都可以被用作一个自定义指令,然后在模板中使用。举一个简单的例子:在输入框渲染后自动聚焦

    <script setup>
    // 在模板中启用 v-focus
    const vFocus = {
      mounted: (el) => el.focus()
    }
    </script>
    
    <template>
      <input v-focus />
    </template>

    运行效果:

  • 如果是使用选项式,则自定义指令需要在directives选项中注册。同上一个例子:

    <script>
    export default{
      setup() {},
      directives: {
        // 指令名
        focus: {
          // 生命周期
          mounted(el) {
            // 处理DOM的逻辑
            el.focus();
          },
        }
      }
    }
    </script>
    <template>
      <input v-focus />
    </template>

    实现的效果也是和上一个例子一样。

  • 除了注册组件内指令,我们还可以自定义全局指令,这样在所有的组件中都可以使用该指令

    // main.js
    import { createApp } from 'vue'
    import App from './App.vue'
    
    const app = createApp(App)
    app.directive('focus', {
      mounted(el) {
        el.focus();
      }
    })
    app.mount('#app')

    实现效果也是一样的。

这三种方式我们选择最后一种,其他两种方式可以按照类似的方式实现。

生命周期

指令的生命周期和组件的生命周期类似:

app.directive('focus', {
  created() {
    console.log('created');
  },
  beforeMount() {
    console.log('beforeMount');
  },
  mounted() {
    console.log('mounted');
  },
  beforeUpdate() {
    console.log('beforeUpdate');
  },
  updated() {
    console.log('updated');
  },
  beforeUnmount() {
    console.log('beforeUnmount');
  },
  unmounted() {
    console.log('unmounted');
  }
})

运行结果:

注意指令没有beforeCreated钩子。

  • created:在绑定元素的属性前,或者事件监听器应用前调用
  • beforeMount:在元素被插入到DOM前调用,例如我们想要实现输入框的自动聚焦,就不能在beforeMount钩子中实现
  • mounted:在绑定元素的父组件以及自己的所有子节点都挂载完毕后调用,这个时候DOM已经渲染出来,我们实现输入框自动聚焦也是在这个钩子函数中实现
  • beforeUpdate:绑定元素的父组件更新前调用
  • updated:在绑定元素的父组件以及自己的所有子节点都更新完毕后调用
  • beforeUnmount:绑定元素的父组件卸载前调用
  • unmounted:绑定元素的父组件卸载后调用

每个钩子函数都有对应的参数,接下来继续看钩子参数。

钩子的参数

指令是为了能重用对DOM的操作逻辑,因此指令参数可以有1-4个参数,其中必需的参数就是当前绑定的DOM元素。

语法:

created(el, binding, vnode, preVnode) {}

参数比较多,我们一个一个来学习

  • el:指令绑定到的DOM元素,可以用于直接操作当前元素,默认传入钩子的就是el参数,例如我们开始实现的focus指令,就是直接操作的元素DOM

  • binding:这是一个对象,包含以下属性:

    • value:在元素上使用指令时,传递给指令的值。例如:<div v-reverse="'hello'"></div>,传递给reserve指令的值就是hello,我们可以拿到值并做相关处理
    • oldValue:之前的值,一般用于beforeUpateupdated钩子函数中,例如:beforeUpdate(el, {oldValue: ''})
    • arg:传递给指令的参数,非必需,例如<div v-reverse:foo="'hello'"></div>,那么传递给指令的参数就是foo
    • modifiers:一个由修饰符构成的对象,例如<div v-reverse.foo.bar="'hello'"></div>,那么该修饰符对象为{foo: true, bar: true},我们经常使用到的事件修饰符,其实和这个差不多。
    • instance:使用该指令的组件实例,注意不是DOM
    • dir:指令的定义对象
  • vnode:绑定元素的地城VNode

  • preVnode:之前的渲染中代表指令所绑定的元素的VNode,一般用于beforeUpateupdated钩子函数中

可能看这些参数会一时迷糊,我们来看一个例子:

定义一个可翻转输入框输入的指令,注意钩子函数要选择beforeUpdate

app.directive('reserve', {
  beforeUpdate(el, binding) {
    console.log(binding);
    el.innerText = binding.value ? binding.value.split('').reverse().join('') : '';
  }
})

在模板中使用:输入框输入值,div会显示反转后的值

<script setup>
import {ref} from 'vue'
let hello = ref('')
</script>
<template>
  <input v-focus v-model="hello" />
  <div v-reserve:foo.bar="hello"></div>
</template>

运行结果:

结合该图,是不是就更能理解钩子参数的含义了。

简化形式

我们在写指令的时候,可以具体指定在哪些钩子中执行一些逻辑。有时候指令的钩子不止一个,但是又是重复的逻辑操作时,重复写一遍代码显然有点不够优雅。在Vue中,如果我们在自定义指令时,需要在mountedupdated中实现相同的行为,并且不关心其他钩子的情况,那么我们开可以采用简写:

app.directive('color', (el, binding) => {
    // 这将会在mounted和updated时调用
    el.style.color = binding.value;
})

对象字面量

我们之前的例子中,传递给指令的值只有一个,如果我们想给指令传入多个值应该怎么操作呢?很简单,传入一个字面量对象即可,可以直接在模板中声明,也可以使用响应式对象,在使用时binding.value就是一个对象了,而不是一个普通的值。

<script setup>
import {ref, Reactive} from 'vue'
let hello = ref('')
const obj = reactive({
  hello: '',
  world: ''
})
</script>
<template>
  <input v-focus v-model="obj.hello" />
  <div v-reserve:foo.bar="obj"></div>
  <!-- <div v-reserve:foo.bar="{hello: obj.hello, world: obj.world}"></div> -->
</template>

对应的,我们的指令也要小小的修改一下:

el.innerText = binding.value ? binding.value.hello.split('').reverse().join('') : '';

实现的效果还是和上面的保持一致。

在组件上使用指令

在元素上直接使用指令,我们可以在指令中操作DOM,这个已经没有问题了。那如果在组件上使用指令会怎样呢?组件其实就是把一些DOM元素封装起来,Vue3和Vue2不同,Vue3的模板中可以不止一个根节点。

我们新建一个Reverse.vue,以便后续作为组件引入。

Vue2:模板中只能有一个根节点,因此会报错

// Reverse.vue
<template>
    <div></div>
    <div></div>
</template>

Vue3:模板中可以不止一个根节点,正常

// Reverse.vue
<template>
    <div></div>
    <div></div>
</template>

既然指令是为了操作DOM元素,如果只有单个根节点那不会有问题,例如:

<script setup>
...
import ReverseVue from './Reserve.vue'
...
</script>
<template>
  ...
  <ReverseVue v-reserve="obj"/>
</template>
// Reverse.vue
<template>
    <!-- v-reserve 指令会被应用在此处 -->
    <div></div>
</template>

如果模板中是多个根节点,就会抛出警告,并且不执行指令

// Reverse.vue
<template>
    <!-- v-reserve 不会作用,并且会抛出警告 -->
    <div></div>
    <div></div>
</template>

结论:尽量不要在组件上使用自定义指令,除非能确定只会有一个根节点

几个实用的自定义指令

以下举例的指令都是全局指令

自动聚焦v-focus

聚焦比较特殊,兄弟元素间只会有一个聚焦,即将该指令作用于两个兄弟输入框上,只会自动聚焦一个

app.directive('focus', (el) => {
    el.focus();
})

防抖v-debounce

在实际项目开发中,经常会听到服务端的同事抱怨:前端怎么不做限流呀。前端做”限流“一般会采用防抖和节流,我们先来看如何实现防抖。

步骤:

  • 首先我们得知道怎么写一个防抖函数
  • 然后需要将防抖函数与el节点绑定,为了通用的话,还需要考虑传入事件类型
  • 最后是卸载定时器等操作
app.directive('debounce', {
  mounted(el, binding) {
    // 至少需要回调函数以及监听事件类型
    if (typeof binding.value.fn !== 'function' || !binding.value.event) return;
    let delay = 200; // 默认延迟时间
    el.timer = null;
    el.handler = function() {
      if (el.timer) {
        clearTimeout(el.timer);
        el.timer = null;
      };
      el.timer = setTimeout(() => {
        binding.value.fn.apply(this, arguments)
        el.timer = null;
      }, binding.value.delay || delay);
    }
    el.addEventListener(binding.value.event, el.handler)
  },
  // 元素卸载前也记得清理定时器并且移除监听事件
  beforeMount(el, binding) {
    if (el.timer) {
      clearTimeout(el.timer);
      el.timer = null;
    }
    el.removeEventListener(binding.value.event, el.handler)
  }
})

在模板中使用:

<script setup>
const handleClick = () => {
  console.log('防抖点击');
}
</script>
<template>
  <button v-debounce="{fn: handleClick, event: 'click', delay: 200}">点击试试</button>
</template>

运行结果:

快速点击按钮并不会立即触发handleClick,而是会在指定的延迟时间后才会触发。

节流v-throttle

节流和防抖类似,都是用于前端”限流“。不同的是,防抖是限制执行次数,多次密集的触发只会执行最后一次,无规律,更关注结果;节流是限制执行频率,有节奏的执行,有规律, 更关注过程。

节流的实现和防抖差不多:

app.directive('throttle', {
  mounted(el, binding) {
    // 至少需要回调函数以及监听事件类型
    if (typeof binding.value.fn !== 'function' || !binding.value.event) return;
    let delay = 200;
    el.timer = null;
    el.handler = function() {
      if (el.timer) return;
      el.timer = setTimeout(() => {
        binding.value.fn.apply(this, arguments)
        el.timer = null;
      }, binding.value.delay || delay);
    }
    el.addEventListener(binding.value.event, el.handler)
  },
  // 元素卸载前也记得清理定时器并且移除监听事件
  beforeMount(el, binding) {
    if (el.timer) {
      clearTimeout(el.timer);
      el.timer = null;
    }
    el.removeEventListener(binding.value.event, el.handler)
  }
})

在模板中使用:

<script setup>
import {reactive} from 'vue'
const obj = reactive({
  hello: '',
  world: ''
})
const handleInput = () => {
  console.log('节流输入框的值:', obj.hello);
}
</script>
<template>
  <input v-throttle="{fn: handleInput, event: 'input', delay: 1000}" v-model="obj.hello" />
</template>

运行结果:

handleInput并不会因为我在输入框输入时的快慢而触发,而是在固定的时间间隔内触发一次,这就是节流。

弹窗隐藏v-hide

在实际开发时会有这样的需求:点击某一个按钮出现一个弹窗,然后点弹窗的其他区域时需要关闭弹窗,如果是点击的弹窗本身,除非是关闭操作,否则不关闭弹窗。

想要实现这种效果,大多数人都会想到全局监听click事件,并且判断点击的目标元素和我们的弹窗元素是不是同一个,如果不是那就隐藏弹窗。那么我们就来看看具体应该怎么实现:

app.directive('hide', {
  mounted(el, binding) {
    el.handler = function(e) {
      // 如果点击范围在绑定的元素范围内,那么将不执行指令操作,而是执行原点击事件
      if (el.contains(e.target)) return;
      if (typeof binding.value.fn === 'function') {
        // 绑定给指令的如果是一个函数,那么将回调并指定this
        binding.value.fn.apply(this, arguments)
        // 并不推荐使用style的方式来隐藏元素,这样的话控制弹窗的变量就无法改变,所以推荐使用回调函数
        // el.style.display = 'none';
        // 解除事件绑定
        document.removeEventListener('click', el.handler)
      }
    }
    // 监听全局的点击事件
    document.addEventListener('click', el.handler)
    // 如果同步绑定全局事件不生效,可以采用异步的方式
    // setTimeout(() => {
    //   document.addEventListener('click', el.handler)
    // }, 0);
  },
  // 解除事件绑定
  beforeMount(el) {
    document.removeEventListener('click', el.handler)
  }
})

在模板中使用:

<script setup>
import {ref} from 'vue'
let isshowModal = ref(false)
const showModal = () => {
  isShowModal.value = true;
}
const cancleModal = () => {
  console.log('cancleModal');
  isShowModal.value = false;
}
</script>
<template>
  <button @click.stop="showModal">点击显示弹窗</button>
  <div class="modal" v-hide="{fn: cancleModal}" v-if="isShowModal">
    <p>我是弹窗</p>
    <button @click.stop="cancleModal">关闭</button>
  </div>
</template>

运行结果:

总结

本文尽量采用通俗易懂的方式,完整的梳理了如何在Vue3中自定义指令。合理的使用指令,可以更快的帮助我们解决问题,值得注意的是:

  • 选择指令的钩子函数时需要明确,不同的钩子函数呈现的效果是不一样的
  • 应该及时卸载钩子函数定义的全局变量、定时器、事件绑定等,避免影响其他组件使用,以及内存泄漏
  • 如果是涉及DOM操作的,我们第一时间应该想到是不是可以抽离成指令的方式

到此这篇关于如何在Vue3中实现自定义指令的文章就介绍到这了,更多相关Vue3自定义指令内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: 如何在Vue3中实现自定义指令(超详细!)

本文链接: https://www.lsjlt.com/news/151134.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何在Vue3中实现自定义指令(超详细!)
    目录前言生命周期钩子的参数简化形式对象字面量在组件上使用指令几个实用的自定义指令自动聚焦v-focus防抖v-debounce节流v-throttle弹窗隐藏v-hide总结在开发V...
    99+
    2022-11-13
  • 怎么在Vue3中实现自定义指令
    这篇文章主要介绍“怎么在Vue3中实现自定义指令”,在日常操作中,相信很多人在怎么在Vue3中实现自定义指令问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么在Vue3中实现自定义指令”的疑惑有所帮助!接下来...
    99+
    2023-07-02
  • vue3自定义指令实现按钮防抖示例详解
    目录写在前面自定义指令实现按钮防抖实现思路代码实现总结写在前面 在实际项目开发过程中,对于按钮的提交事件来说,我们需要限制按钮重复点击,避免在某个时间内重复提交。这时候我们一般用到...
    99+
    2023-02-27
    vue3自定义指令按钮防抖 vue 按钮防抖
  • vue3中怎么自定义指令实现按钮防抖
    这篇“vue3中怎么自定义指令实现按钮防抖”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“vue3中怎么自定义指令实现按钮防抖...
    99+
    2023-07-05
  • Vue3中的setup与自定义指令如何使用
    本篇内容主要讲解“Vue3中的setup与自定义指令如何使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Vue3中的setup与自定义指令如何使用”吧!setup语法糖最大好处就是所有声明部分...
    99+
    2023-07-06
  • vue3使用自定义指令实现eldialog拖拽功能示例详解
    目录实现el-dialog的拖拽功能通过自定义指令实现拖拽功能实现拖拽功能使用方式实现el-dialog的拖拽功能 这里指的是 element-plus 的el-dialog组件,一...
    99+
    2022-11-13
  • 关于Vue自定义指令实现元素拖动的详细代码
    昨天在做的一个功能时,同时弹出多个框展示多个表格数据。 这些弹出框可以自由拖动。单独的拖动好实现,给元素绑定 mousedowm 事件。 这里就想到了 Vue 里面自定义指令来实现。...
    99+
    2022-11-13
  • 如何通过Vue自定义指令实现前端埋点详析
    目录前言埋点上报方式都有哪些?一般对哪些数据做埋点?需求分析代码实现Click 类封装Exposure 类封装指令封装使用不足总结前言 在营销活动中,通过埋点可以获取用户的喜好及交互...
    99+
    2022-09-27
  • Vue中在setup下如何使用自定义指令
    目录如何在setup下使用自定义指令1. 局部的自定义指令2. 全局注册自定义指令3. 简单的效果图4. 千万要注意如何在setup下使用自定义指令 1. 局部的自定义指令 html...
    99+
    2022-11-13
  • Vue如何自定义指令实现元素拖动
    这篇文章主要介绍了Vue如何自定义指令实现元素拖动的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue如何自定义指令实现元素拖动文章都会有所收获,下面我们一起来看看吧。一、自定义指令在使用自定义指令之前,先对自...
    99+
    2023-06-26
  • Vue自定义指令如何实现checkbox全选功能
    这篇文章主要介绍Vue自定义指令如何实现checkbox全选功能,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!代码:<!DOCTYPE html> <ht...
    99+
    2022-10-19
  • 如何在vue中使用过滤器和自定义指令
    本篇文章给大家分享的是有关如何在vue中使用过滤器和自定义指令,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。过滤器过滤器可以对我们传入的数据进行必要的处理,并返回处理的结果过滤...
    99+
    2023-06-15
  • angularJS如何实现自定义指令间的相互交互
    小编给大家分享一下angularJS如何实现自定义指令间的相互交互,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!AngularJ...
    99+
    2022-10-19
  • Vue+element自定义指令如何实现表格横向拖拽
    目录element自定义指令实现表格横向拖拽自定义指令设置及常用自定义指令指令定义函数提供了几个钩子函数(可选)下面分享几个实用的 Vue 自定义指令element自定义指令实现表格...
    99+
    2022-11-13
    Vue element 自定义指令 表格横向拖拽
  • VUE3中如何实现拖拽与缩放自定义看板
    本文小编为大家详细介绍“VUE3中如何实现拖拽与缩放自定义看板”,内容详细,步骤清晰,细节处理妥当,希望这篇“VUE3中如何实现拖拽与缩放自定义看板”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1. npm下载拖...
    99+
    2023-07-05
  • 详解Vue自定义指令如何实现处理图片加载失败的碎图
    目录一、自定义指令1、局部注册和使用2、全局注册和使用二、自定义指令处理图片加载失败(碎图)一、自定义指令 vue中除v-model、v-show等内置指令之外,还允许注册自定义指令...
    99+
    2023-02-15
    Vue自定义指令 Vue处理碎图 Vue图片加载失败
  • Vue中如何实现自定义指令上报Google Analytics事件统计功能
    这篇文章将为大家详细讲解有关Vue中如何实现自定义指令上报Google Analytics事件统计功能,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。发现问题一般前端开发离...
    99+
    2022-10-19
  • 基于Vue自定义指令如何实现按钮级权限控制
    这篇文章将为大家详细讲解有关基于Vue自定义指令如何实现按钮级权限控制,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。思路:登录:当用户填写完账号和密码后向服务端验证是否正...
    99+
    2022-10-19
  • Angular.js如何通过自定义指令directive实现滑块滑动效果
    这篇文章主要介绍Angular.js如何通过自定义指令directive实现滑块滑动效果,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!方法如下1.下面是我html部分代码,detai...
    99+
    2022-10-19
  • vue2如何实现directive自定义指令的封装与全局注册
    本文小编为大家详细介绍“vue2如何实现directive自定义指令的封装与全局注册”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue2如何实现directive自定义指令的封装与全局注册”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作