Vue.directive:自定义指令及传参

Vue官方提供很多指令,比如:v-model,v-show,v-if,v-on等,他们都以v-开头。当这些指令不能满足实际开发需求时,我们可以自定义指令,包括全局自定义指令和局部自定义指令。聚焦于底层DOM操作,用于修改元素的样式、操作DOM、处理事件等。

一、指令封装

1、 v-dialogDragWidth: 拖拽 el-dialog 弹窗宽度拖大、 拖小

复制代码
import Vue from 'vue'
 
// v-dialogDragWidth: 弹窗宽度拖大 拖小
Vue.directive('dialogDragWidth', {
  bind(el, binding, vnode, oldVnode) {
    const dragDom = binding.value.$el.querySelector('.el-dialog')
 
    el.onmousedown = (e) => {
      // 鼠标按下,计算当前元素距离可视区的距离
      const disX = e.clientX - el.offsetLeft
 
      document.onmousemove = function(e) {
        e.preventDefault() // 移动时禁用默认事件
 
        // 通过事件委托,计算移动的距离
        const l = e.clientX - disX
        dragDom.style.width = `${l}px`
      }
 
      document.onmouseup = function(e) {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
})

2、v-lazy :图片懒加载,监听scroll事件并计算元素位置

复制代码
import Vue from 'vue'

// v-lazy: 图片懒加载
Vue.directive('lazy', {
  inserted(el, binding) {
    const observer = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting) {
        el.src = binding.value
        observer.unobserve(el)
      }
    })
    observer.observe(el)
  }
})
二、指令使用

1、在 utils 中新建 directives.js 文件

2、main.js中导入:import './utils/directives.js'

3、使用指令的地方加入 dialogDragWidth 、v-lazy

复制代码
<el-dialog
   :visible.sync="dialogVisible"
   v-dialogDragWidth>
   // ......
</el-dialog>


<img v-lazy="detail.imageUrl">
三、指令扩展
1. 指令钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。

inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。

componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

unbind:只调用一次,指令与元素解绑时调用。

每个钩子函数有以下参数:
  1. el:指令所绑定的元素,可以用来直接操作 DOM。
  2. binding:一个对象,包含以下 property:

name:指令名,不包括 v- 前缀。

value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。

oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。

expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。

arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。

modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。

vnode:Vue 编译生成的虚拟节点。

oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

代码实现:
复制代码
import Vue from 'vue'

Vue.directive('tooltip',{
    bind(el, binding) {
      // 当指令绑定到 HTML 元素上时触发.**只调用一次,适合进行一次性初始化**
      console.log('bind triggerd')
      el.style.position = 'relative'
      const tooltip = document.createElement('div')
      tooltip.className = 'tooltip'
      tooltip.textContent = binding.value
      el._tooltip = tooltip
      document.body.appendChild(tooltip)
    },
    inserted(el, binding) {
      // 当绑定了指令的这个HTML元素插入到父元素上时触发(在这里父元素是 `div#app`)**.但不保证,父元素已经插入了 DOM 文档.**。若父元素初始为 display:none,获取的尺寸可能不准确,需结合MutationObserver监听显示状态变化。
      console.log('inserted triggerd')
      const rect = el.getBoundingClientRect()
      el._tooltip.style.cssText = `
        position: absolute;
        left: ${rect.left + rect.width/2}px;
        top: ${rect.top - 30}px;
      `
    },
    updated(el, binding) {
      // 所在组件的`VNode`更新时调用. 但会忽略首次绑定
      console.log('updated triggerd')
      if (binding.value !== binding.oldValue) {
        el._tooltip.textContent = binding.value
      }
    },
    componentUpdated(el, binding) {
      // 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
      console.log('componentUpdated triggerd')
      // 适合在子组件更新后重新计算位置
      const rect = el.getBoundingClientRect()
      // 更新tooltip位置...
    },
    unbind(el, binding) {
      // 只调用一次,指令与元素解绑时调用. 必须进行资源释放,若忘记移除事件监听器或全局对象,会导致内存泄漏
      console.log('unbind triggerd')
      if (el._tooltip) {
        document.body.removeChild(el._tooltip)
        delete el._tooltip
      }
    }
  })
四、指令参数传参
1、传递静态参数

在自定义指令中传递静态参数,静态参数是指在指令绑定时已知的参数值。在HTML标签上使用指令时,使用冒号(:)来传递静态参数。例如,自定义指令叫做"v-dialogDragWidth",我们可以通过这样的方式传递一个静态参数:

<div v-dialogDragWidth:arg="value"></div>

给 v-dialogDragWidth 指令传递了一个名为"arg"的静态参数,它的值为"value"。

我们可以在自定义指令的定义中通过"bind"钩子函数获取传入的参数,并在绑定元素上进行相应的操作:

Vue.directive('dialogDragWidth', {

bind: function(el, binding) {

console.log(binding.arg); // 输出:arg

console.log(binding.value); // 输出:value

// 在这里可以对绑定元素进行操作

}

});

2、传递动态参数

动态参数是指在指令绑定时不确定的参数值,根据不同的情况动态改变。在Vue中,我们可以使用方括号([])来传递动态参数:

<div v-dialogDragWidth:[arg]="value"></div>

或者

<div v-dialogDragWidth:[arg1,arg2,arg3]="value"></div>

我们给" v-dialogDragWidth "指令传递了一个动态参数,它的值为"value"。在Vue实例中我们可以在数据中定义"arg"的值,并在渲染的时候动态改变它。例如:

data: {

arg: 'dynamicArg'

}

当数据中的"arg"发生变化时,指令中的动态参数也会相应地改变。在自定义指令的定义中,我们可以通过表达式来获取动态参数的值。

Vue.directive('dialogDragWidth', {

bind: function(el, binding) {

console.log(binding.arg); // 输出:dynamicArg

console.log(binding.value); // 输出:value

// 在这里可以对绑定元素进行操作

}

});

3、传参场景使用

通过对象形式动态传递参数,实现更灵活的权限控制:

// 使用方式:

<el-button v-permission:{"您没有删除权限"}="'delete'">删除</el-button>

// 数据处理:

Vue.directive('permission', {

bind(el, binding) {

const { value, arg } = binding // value:权限字符串,arg:错误提示

if (!checkPermission(value)) {

el.style.display = 'none'

el._permissionDenied = arg || '无权限访问'

}

}

})

相关推荐
wangchen_02 小时前
C++<fstream> 深度解析:文件 I/O 全指南
开发语言·前端·c++
程序员码歌2 小时前
短思考第266天,玩IP路上的几点感悟,这几点很重要!
前端·后端·创业
2501_946230982 小时前
Cordova&OpenHarmony用户账户管理
android·javascript
梵得儿SHI2 小时前
2025 Vue 技术实战全景:从工程化到性能优化的 8 个落地突破
前端·javascript·vue.js·pinia2.2·响应式数据分片·展望vue3.6·2025年vue技术栈
熊猫钓鱼>_>2 小时前
解决Web游戏Canvas内容在服务器部署时的显示问题
服务器·前端·游戏·canvas·cors·静态部署·资源路径
梦6502 小时前
React 封装 UEditor 富文本编辑器
前端·react.js·前端框架
Hao_Harrision2 小时前
50天50个小项目 (React19 + Tailwindcss V4) ✨ | DoubleClickHeart(双击爱心)
前端·typescript·react·tailwindcss·vite7
qq. 28040339842 小时前
react 编写规范
前端·react.js·前端框架
qq. 28040339842 小时前
react 基本语法
前端·react.js·前端框架