Vue自定义指令:从入门到实战应用

221 阅读3分钟

一、自定义指令基础概念

官方文档:自定义指令

1.1 什么是自定义指令?

Vue自定义指令是对原生DOM操作的高级封装,用于扩展HTML元素的功能。它允许开发者创建可复用的DOM行为,主要应用于以下场景:

  • DOM底层操作(如聚焦、拖拽)
  • 集成第三方DOM库
  • 创建可视化特效

1.2 生命周期钩子

钩子函数触发时机典型应用场景
created元素属性初始化时访问初始化属性值
beforeMount元素插入DOM前预加载资源
mounted元素插入DOM后DOM操作(最常用)
beforeUpdate绑定值更新前保存旧状态
updated绑定值更新后响应数据变化
beforeUnmount元素卸载前清理定时器
unmounted元素卸载后移除事件监听

二、指令注册方式详解

2.1 全局注册

// main.js
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

// 方式1:直接注册
app.directive('focus', { //focus指令
  mounted(el) {  //mounted时生效
    el.focus()
  }
})

// 方式2:模块化注册  //代码更加优雅
import draggable from './directives/draggable'
app.directive('draggable', draggable)

2.2 局部注册(组件级)

<script setup>
// 方式1:函数式指令
const vColor = (el, binding) => {
  el.style.color = binding.value
}

// 方式2:对象式指令
const vHighlight = {
  mounted(el, binding) {
    el.style.backgroundColor = binding.value
  },
  updated(el, binding) {
    el.style.backgroundColor = binding.value
  }
}
</script>

三、实战案例解析

3.1 案例一:元素拖拽指令

3.1.1. 定义指令

// 定义自定义指令
export default {
  mounted(el) {
    // 鼠标放上去时,改变鼠标样式为“move”
    el.style.cursor = 'move'
    el.style.position = 'relative'

    // 监听鼠标按下事件
    el.onmousedown = function(e) {
      // 获取鼠标按下时相对元素的偏移
      let disX = e.pageX - el.offsetLeft
      let disY = e.pageY - el.offsetTop

      // 监听鼠标移动事件
      document.onmousemove = function(e) {
        let x = e.pageX - disX
        let y = e.pageY - disY

        // 边界检测
        let maxX = document.body.clientWidth - el.offsetWidth
        let maxY = document.body.clientHeight - el.offsetHeight

        // 限制元素在页面中的最大和最小位置
        if (x < 0) x = 0
        if (x > maxX) x = maxX
        if (y < 0) y = 0
        if (y > maxY) y = maxY

        // 设置元素的新位置
        el.style.left = `${x}px`
        el.style.top = `${y}px`
      }

      // 监听鼠标释放事件
      document.onmouseup = function() {
        // 取消事件
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
}

3.1.2. 注册指令

指令的定义需要一个install函数,该函数用于注册指令到Vue实例中。Vue会调用install函数来将指令注册到全局或局部。

//  ./command/index.js'
import Draggable from './draggable.js'
// 统一注册
const directive = {
  Draggable
  //...
  //...
}

export default {
  install(app) {
    // 注册自定义指令
    Object.keys(directive).forEach(key => {
      app.directive(key, directive[key])  //指令需要通过`app.directive`来注册。
    })
  }
}

然后在main.js中挂载。

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import Directives from './command/index.js'

const app = createApp(App)
app.use(Directives) // 全局注册 // 注册指令use调用了install 函数,触发注册
app.mount('#app')

3.1.3. 在组件中使用指令

自定义指令注册后,你就可以在模板中使用它了。假设你创建了一个v-draggable指令来使元素可拖拽,下面是如何使用它的示例:

<template>
  <div class="box" v-draggable></div>
</template>

<script setup>
// 这里不需要特别的代码,因为指令已经在全局注册过
</script>

<style lang="css" scoped>
.box {
  width: 100px;
  height: 100px;
  background-color: #f00;
}
</style>

3.2 案例二:动态颜色指令

// 对象式写法(推荐)
export default {
// el:该指令绑定在哪个元素上   binding:指令的相关信息,包含该指令绑定的值
  mounted(el, binding) {  
    updateColor(el, binding.value);
  },
  updated(el, binding) {
    updateColor(el, binding.value);
  }
}

// 函数式写法(同时触发mounted和updated)
// export default (el, binding) => {
//   updateColor(el, binding.value)
// }

function updateColor(el, value) {
  const length = value?.length || 0;
  
  if (length <= 5) {
    el.style.color = 'green';
  } else if (length <= 10) {
    el.style.color = 'goldenrod';
  } else {
    el.style.color = 'red';
  }
}

3.1.3. 使用示例

<template>
  <div class="input-group">
    <input 
      type="text" 
      v-model="inputVal"
      placeholder="输入内容观察颜色变化"
    >
    <h3 v-highlight="inputVal">{{ inputVal }}</h3>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const inputVal = ref('');
</script>

<style>
.input-group {
  margin: 20px;
  padding: 15px;
  border: 1px solid #eee;
}

h3 {
  transition: color 0.3s;
  padding: 10px;
  border-radius: 4px;
}
</style>