vue3从精通到入门21:自定义指令directive

Vue3中的自定义指令directive是一种扩展HTML标签能力的机制。通过自定义指令,开发者可以直接在模板中使用指令名,并为其提供相应的逻辑。这些指令在Vue应用程序中扮演着重要角色,允许开发者执行一些低级DOM操作或访问元素状态。

自定义指令的钩子函数

自定义指令可以包含多个钩子函数,它们在指令的不同生命周期阶段被调用:

  • beforeMount(el, binding, vnode, prevVnode): 在元素被插入到 DOM 之前调用。
  • mounted(el, binding, vnode, prevVnode): 在元素被插入到 DOM 后调用。
  • beforeUpdate(el, binding, vnode, prevVnode): 在元素更新之前调用。
  • updated(el, binding, vnode, prevVnode): 在元素更新后调用。
  • beforeUnmount(el, binding, vnode, prevVnode): 在元素被移除之前调用。
  • unmounted(el, binding, vnode, prevVnode): 在元素被移除后调用。

这些钩子函数都接收相同的参数:

  • el: 指令所绑定的元素,可以用来直接操作 DOM。
  • binding: 一个对象,包含以下属性:
    • 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 虚拟节点。
  • prevVnode: 上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

使用方法

1. 定义自定义指令 

直接定义自定义指令,并通过 defineDirective 函数将其注册为局部指令。

<template>  
  <input v-focus />  
</template>  
  
<script setup lang="ts">  
import { defineDirective } from 'vue'  
  
// 定义一个名为 'focus' 的自定义指令  
const focusDirective = defineDirective({  
  // 当元素被插入到 DOM 中时调用  
  mounted(el: HTMLElement) {  
    el.focus()  
  },  
  // 当元素更新时调用  
  updated(el: HTMLElement) {  
    // 这里可以添加更新时的逻辑,比如重新聚焦等  
  }  
})  
  
// 注册局部自定义指令  
defineDirective('focus', focusDirective)  
</script>

在上面的代码中,我们首先通过 import { defineDirective } from 'vue' 导入了 defineDirective 函数。然后,我们定义了一个名为 focusDirective 的对象,它包含了自定义指令的钩子函数。最后,我们使用 defineDirective 函数将 focusDirective 注册为名为 focus 的局部自定义指令。

2. 使用自定义指令

<template>  
  <input v-focus placeholder="这个输入框会自动聚焦" />  
</template>

3. 带参数的自定义指令

<template>  
  <div v-scroll-to="'top'">滚动到顶部</div>  
  <div v-scroll-to="'bottom'">滚动到底部</div>  
</template>  
  
<script setup lang="ts">  
import { defineDirective, onMounted, onUpdated, ref } from 'vue'  
  
const scrollToDirective = defineDirective({  
  mounted(el, binding) {  
    const scrollTarget

使用场景

我们在实际项目中,会遇到各种场景,以下是典型场景:

  1. 焦点管理:自动设置输入框的焦点。
  2. 元素拖拽:使元素可以拖拽。
  3. 图片懒加载:延迟加载图片直到它们进入视口。
  4. 文本高亮:根据条件高亮文本中的特定部分。
  5. 权限控制:基于用户权限控制元素的显示或隐藏。

焦点管理已经在上述例子中讲解过;

1. 元素拖拽

<template>  
  <div id="app">  
    <div v-drag>Drag me</div>  
  </div>  
</template>  
  
<script setup lang="ts">  
import { ref, onMounted, onUnmounted } from 'vue';  
  
// 自定义指令 v-drag  
const dragDirective = {  
  mounted(el: HTMLElement) {  
    let offsetX = 0;  
    let offsetY = 0;  
    let isDragging = false;  
  
    const handleMousedown = (event: MouseEvent) => {  
      offsetX = event.clientX - el.offsetLeft;  
      offsetY = event.clientY - el.offsetTop;  
      isDragging = true;  
      document.addEventListener('mousemove', handleMousemove);  
      document.addEventListener('mouseup', handleMouseup);  
    };  
  
    const handleMousemove = (event: MouseEvent) => {  
      if (!isDragging) return;  
      const x = event.clientX - offsetX;  
      const y = event.clientY - offsetY;  
      el.style.transform = `translate(${x}px, ${y}px)`;  
    };  
  
    const handleMouseup = () => {  
      isDragging = false;  
      document.removeEventListener('mousemove', handleMousemove);  
      document.removeEventListener('mouseup', handleMouseup);  
    };  
  
    el.addEventListener('mousedown', handleMousedown);  
  },  
  unmounted() {  
    // 清理事件监听器  
    document.removeEventListener('mousemove', handleMousemove);  
    document.removeEventListener('mouseup', handleMouseup);  
  }  
};  
  
// 注册自定义指令  
const drag = directive(dragDirective);  
</script>  
  
<style scoped>  
#app {  
  width: 100%;  
  height: 100vh;  
  display: flex;  
  justify-content: center;  
  align-items: center;  
}  
div {  
  width: 100px;  
  height: 100px;  
  background-color: skyblue;  
  cursor: move;  
  user-select: none; /* 禁止文本选择 */  
}  
</style>

2. 图片懒加载

<template>  
  <div id="app">  
    <img v-lazy="imageSrc" alt="Lazy loaded image">  
  </div>  
</template>  
  
<script setup lang="ts">  
import { ref, onMounted, onUnmounted } from 'vue';  
  
// 自定义指令 v-lazy  
const lazyDirective = {  
  mounted(el: HTMLImageElement, binding: any) {  
    const observer = new IntersectionObserver((entries, observer) => {  
      entries.forEach(entry => {  
        if (entry.isIntersecting) {  
          // 当图片进入视口时,设置图片的src属性  
          el.src = binding.value;  
          // 停止观察  
          observer.unobserve(el);  
        }  
      });  
    },  
    { threshold: 0.1 } // 提前加载,可以设置阈值  
  );  
  
  observer.observe(el);  
},  
unmounted() {  
  // 组件卸载时,停止观察  
  observer.unobserve(el);  
}  
};  
  
// 注册自定义指令  
const lazy = directive(lazyDirective);  
  
const imageSrc = ref('path/to/your/image.jpg'); // 图片地址  
</script>  
  
<style scoped>  
#app {  
  width: 100%;  
  height: 100vh;  
  display: flex;  
  justify-content: center;  
  align-items: center;  
}  
img {  
  width: 300px;  
  height: 200px;  
}  
</style>

3. 文本高亮

<template>  
  <div id="app">  
    <p v-highlight="'Vue 3'">Vue 3 is awesome!</p>  
  </div>  
</template>  
  
<script setup lang="ts">  
import { Directive } from 'vue';  
  
// 自定义指令 v-highlight  
const highlightDirective: Directive = {  
  // 当绑定元素插入到 DOM 中。  
  mounted(el, binding) {  
    // 获取绑定值,即要高亮显示的文本  
    const searchTerm = binding.value.toString();  
  
    // 创建一个正则表达式,用于全局不区分大小写的匹配  
    const regex = new RegExp(searchTerm, 'gi');  
  
    // 获取元素的文本内容  
    const textContent = el.textContent || '';  
  
    // 使用replace方法替换匹配到的文本,并添加高亮样式  
    const highlightedText = textContent.replace(regex, (matchedText) => {  
      return `<mark>${matchedText}</mark>`;  
    });  
  
    // 设置元素的innerHTML为高亮后的文本  
    el.innerHTML = highlightedText;  
  },  
  
  // 当绑定元素更新时  
  updated(el, binding) {  
    // 更新文本高亮  
    this.mounted(el, binding);  
  }  
};  
  
// 注册自定义指令  
const highlight = directive(highlightDirective);  
</script>  
  
<style scoped>  
#app {  
  width: 100%;  
  height: 100vh;  
  display: flex;  
  justify-content: center;  
  align-items: center;  
}  
mark {  
  background-color: yellow;  
}  
</style>

4. 权限控制

<template>  
  <div id="app">  
    <!-- 使用v-permission指令,参数为所需的权限 -->  
    <button v-permission="'admin'">Admin Only Button</button>  
    <button v-permission="'user'">User Button</button>  
  </div>  
</template>  
  
<script setup lang="ts">  
import { ref, onMounted, Directive } from 'vue';  
  
// 假设这是从后端或其他地方获取的当前用户的权限  
const userPermissions = ref(['user']);  
  
// 自定义指令 v-permission  
const permissionDirective: Directive = {  
  mounted(el, binding) {  
    // 获取指令的绑定值,即所需的权限  
    const requiredPermission = binding.value;  
  
    // 检查用户是否有权限  
    if (!userPermissions.value.includes(requiredPermission)) {  
      // 如果没有权限,隐藏元素  
      el.style.display = 'none';  
    }  
  },  
  updated(el, binding) {  
    // 当指令的值更新时,重新检查权限  
    this.mounted(el, binding);  
  }  
};  
  
// 注册自定义指令  
const permission = directive(permissionDirective);  
  
// 示例:模拟从后端获取用户权限  
onMounted(() => {  
  // 假设这是从后端API或其他地方获取权限的模拟  
  // 在实际应用中,你会在用户登录或其他时机获取这些权限  
  setTimeout(() => {  
    // 假设当前用户只有'user'权限  
    userPermissions.value = ['user'];  
  }, 1000);  
});  
</script>  
  
<style scoped>  
#app {  
  width: 100%;  
  height: 100vh;  
  display: flex;  
  flex-direction: column;  
  align-items: center;  
  justify-content: center;  
}  
</style>

### Vue2 入门精通教程 以下是关于 Vue2 的全面知识介绍,涵盖了从基础概念到高级技巧的内容。 #### 1. 基础概念 Vue 是一个用于构建用户界面的渐进式框架。通过数据绑定和组件化开发模式,开发者可以轻松创建复杂的前端应用。以下是一个简单的 Vue 实例: ```html <body> <!-- 准备好一个容器 --> <div id="root"> <h1>Hello, {{ name }}, you are from {{ address }}</h1> </div> <!-- 创建一个 Vue 实例 --> <script> new Vue({ el: "#root", data: { name: "Vue2.0", address: "Beijing" } }); </script> </body> ``` 此代码展示了如何初始化一个 Vue 实例并将其挂载到 DOM 上[^2]。 #### 2. 数据绑定与插值表达式 Vue 提供了强大的双向数据绑定功能,使得视图能够自动更新以反映模型的变化。`{{ }}` 插值语法可用于动态渲染 HTML 内容。 #### 3. 注册全局组件 为了提高代码复用性和模块化程度,可以通过 `Vue.component()` 方法注册全局组件。例如: ```javascript // 注册全局组件 Vue.component(&#39;my-button&#39;, { data: function () { return { count: 0 }; }, template: &#39;<button @click="count++">Clicked {{ count }} times.</button>&#39; }); ``` 这段代码定义了一个名为 `my-button` 的按钮组件,并实现了点击计数的功能[^1]。 #### 4. 自定义指令 除了内置指令外,还可以自定义全局或局部指令来扩展 Vue 功能。比如实现聚焦效果: ```javascript // 注册全局指令 Vue.directive(&#39;focus&#39;, { inserted: function (el) { el.focus(); } }); ``` 当某个元素被插入文档时会触发该指令的行为[^1]。 #### 5. 单文件组件(SFC) 随着项目复杂度增加,推荐使用 `.vue` 扩展名表示单文件组件形式组织代码结构。这种格式支持在同一文件中声明模板、脚本以及样式部分: ```vue <template> <h3>Single File Component Example</h3> </template> <script> export default { name: &#39;MyComponent&#39; }; </script> <style scoped> h3 { color: blue; } </style> ``` 以上例子展示了一种典型的 SFC 结构及其加载方式[^3]。 #### 高级特性概览 - **计算属性**: 计算属性允许基于其他响应式依赖项派生新状态。 - **侦听器(watch)**: 当某些特定的数据发生变化时执行异步操作或者开销较大的操作非常有用。 - **生命周期钩子函数**: 可以在不同阶段介入实例生命过程中的行为调整。 - **路由管理(Vue Router)** 和 **状态管理模式 Vuex** : 这些工具帮助处理更大型的应用程序需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值