当前位置: 首页 > news >正文

Vue 自定义指令 Directive 的高级使用与最佳实践

前言

Vue.js 是一个非常流行的前端框架,它的核心理念是通过声明式的方式来描述 UI 和数据绑定。除了模板语法和组件系统,Vue 还提供了一个强大的功能——自定义指令。
自定义指令可以让我们对 DOM 元素进行底层操作,下面让我们通过一个有趣的例子来看看如何使用自定义指令。

什么是自定义指令?

在 Vue.js 中,指令(Directive)是带有 v- 前缀的特殊属性。当表达式的值改变时,指令会对 DOM 进行相应的更新。Vue 内置了一些常用的指令,比如 v-model、v-if、v-for 等等。
自定义指令让我们可以定义自己的逻辑,对 DOM 元素进行任意的操作,比如添加事件监听器、操作样式、进行动画等等。

自定义步骤

如何创建自定义指令?

创建自定义指令非常简单。我们只需要在 Vue 实例上调用 directive 方法,并传入指令的名称和钩子函数。下面是一个简单的例子:

Vue.directive('focus', {// 当绑定元素插入到 DOM 中时inserted: function (el) {el.focus();}
});

在上面的例子中,我们创建了一个名为 v-focus 的自定义指令,这个指令会在绑定的元素插入到 DOM 中时自动获取焦点。

自定义指令的钩子函数

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

  • bind: 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  • inserted: 当绑定元素插入到 DOM 中时调用。
  • update: 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生改变,也可能没有。
  • componentUpdated: 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind: 只调用一次,指令与元素解绑时调用。

让我们来实现一个更复杂的自定义指令:点击改变背景色。

Vue.directive('color-swap', {bind: function (el, binding) {el.style.backgroundColor = binding.value;el.addEventListener('click', function () {el.style.backgroundColor = el.style.backgroundColor === 'red' ? 'blue' : 'red';});},unbind: function (el) {el.removeEventListener('click');}
});

在上面的例子中,我们创建了一个名为 v-color-swap 的自定义指令,这个指令会在元素被点击时,切换背景色。

使用自定义指令

使用自定义指令非常简单,只需要在模板中使用 v- 前缀加上指令名即可:

<div v-focus></div>
<button v-color-swap="'red'">Click me to change color</button>

在这个模板中,我们使用了 v-focus 指令让 div 元素自动获取焦点,并使用了 v-color-swap 指令让按钮在点击时切换背景色。

高级用法

1. 自定义拖拽指令

为了更好地理解自定义指令,我们来构建一个更复杂的示例。这次我们将创建一个自定义指令,用于实现拖拽元素。
我们希望通过自定义指令,使得任何绑定了该指令的元素都能够被拖拽。下面是完整的实现代码:

Vue.directive('drag', {bind: function (el) {el.style.position = 'absolute';el.style.cursor = 'move';el.onmousedown = function (event) {// 获取鼠标按下时的坐标和元素初始位置let startX = event.clientX;let startY = event.clientY;let initialLeft = parseInt(el.style.left || 0);let initialTop = parseInt(el.style.top || 0);document.onmousemove = function (event) {// 计算移动距离let dx = event.clientX - startX;let dy = event.clientY - startY;// 更新元素位置el.style.left = initialLeft + dx + 'px';el.style.top = initialTop + dy + 'px';};document.onmouseup = function () {// 移除事件监听器document.onmousemove = null;document.onmouseup = null;};// 防止默认行为导致的选择文字等event.preventDefault();};},unbind: function (el) {el.onmousedown = null;}
});

有了这个自定义指令,我们可以在模板中轻松地使元素具备拖拽功能:

<div v-drag style="width: 100px; height: 100px; background-color: lightcoral;">Drag me!
</div>

现在,当你运行这个页面时,这个 div 元素就可以被拖拽了。

2. 自定义指令的参数

自定义指令还可以接受参数和修饰符。让我们扩展一下拖拽指令,使得它可以接受一个参数,用于限制拖拽的方向。

Vue.directive('drag', {bind: function (el, binding) {el.style.position = 'absolute';el.style.cursor = 'move';el.onmousedown = function (event) {let startX = event.clientX;let startY = event.clientY;let initialLeft = parseInt(el.style.left || 0);let initialTop = parseInt(el.style.top || 0);document.onmousemove = function (event) {let dx = event.clientX - startX;let dy = event.clientY - startY;if (binding.arg === 'x') {el.style.left = initialLeft + dx + 'px';} else if (binding.arg === 'y') {el.style.top = initialTop + dy + 'px';} else {el.style.left = initialLeft + dx + 'px';el.style.top = initialTop + dy + 'px';}};document.onmouseup = function () {document.onmousemove = null;document.onmouseup = null;};event.preventDefault();};},unbind: function (el) {el.onmousedown = null;}
});

在这个扩展的例子中,我们添加了对 binding.arg 的检查,以决定拖拽的方向。如果参数是 x,则只允许水平拖拽;如果参数是 y,则只允许垂直拖拽;否则,允许任意方向的拖拽。

使用带参数的拖拽指令

<div v-drag:x style="width: 100px; height: 100px; background-color: lightcoral;">Drag me horizontally!
</div>
<div v-drag:y style="width: 100px; height: 100px; background-color: lightblue;">Drag me vertically!
</div>

现在,我们有两个 div,一个只能水平拖动,另一个只能垂直拖动。

3. 双向绑定与修饰符

在实际开发中,我们经常需要处理更复杂的场景。接下来,我们将介绍如何利用 Vue 自定义指令实现一个双向绑定的输入框,并且通过修饰符来控制输入的格式(比如只能输入数字)。

我们先创建一个基本的双向绑定输入框指令,这个指令将输入框的值与 Vue 实例中的数据进行绑定:

Vue.directive('model', {bind: function (el, binding, vnode) {el.value = binding.value;el.addEventListener('input', function (event) {vnode.context[binding.expression] = event.target.value;});},update: function (el, binding) {el.value = binding.value;}
});

在这个例子中,我们使用 v-model 这个指令来实现双向绑定。当绑定的值变化时,我们通过 update 钩子函数来更新输入框的值;当输入框的值变化时,我们通过 input 事件来更新 Vue 实例中的数据。

使用双向绑定输入框指令

<div id="app"><input v-model="message" /><p>{{ message }}</p>
</div><script>new Vue({el: '#app',data: {message: ''}});
</script>

通过这个模板,我们可以实现一个简单的双向绑定输入框。当用户在输入框中输入内容时,下方的 p 元素会实时显示当前的输入内容。

使用修饰符控制输入格式
我们可以进一步扩展这个自定义指令,使用修饰符来控制输入的格式。比如,我们只允许用户输入数字:

Vue.directive('model', {bind: function (el, binding, vnode) {el.value = binding.value;el.addEventListener('input', function (event) {let value = event.target.value;if (binding.modifiers.number) {value = value.replace(/[^\d]/g, '');}vnode.context[binding.expression] = value;});},update: function (el, binding) {el.value = binding.value;}
});

在这个扩展的例子中,我们检查 binding.modifiers.number 是否存在。如果存在,我们通过正则表达式只保留数字字符。

使用带修饰符的双向绑定输入框指令

<div id="app"><input v-model.number="message" /><p>{{ message }}</p>
</div><script>new Vue({el: '#app',data: {message: ''}});
</script>

现在,当用户在输入框中输入非数字字符时,这些字符会被自动过滤掉,确保 message 始终是一个数字字符串。

最佳实践

1. 命名规范

为了避免与内置指令冲突,自定义指令的名称应当尽量具有唯一性和描述性。例如,我们可以在自定义指令前添加项目特定的前缀:

Vue.directive('my-focus', {// ...
});

2. 为指令添加清理逻辑

如果自定义指令添加了事件监听器或其他副作用,一定要在 unbind 钩子函数中清理这些副作用,以避免内存泄漏:

Vue.directive('my-event', {bind: function (el, binding) {el.addEventListener('click', binding.value);},unbind: function (el, binding) {el.removeEventListener('click', binding.value);}
});

3. 使用对象字面量传递多个参数

有时我们可能需要为指令传递多个参数,这时可以使用对象字面量的方式:

Vue.directive('highlight', {bind: function (el, binding) {const { color, backgroundColor } = binding.value;el.style.color = color;el.style.backgroundColor = backgroundColor;}
});

在模板中使用:

<p v-highlight="{ color: 'white', backgroundColor: 'blue' }">Highlighted Text</p>

4. 使用指令钩子函数

Vue 自定义指令提供了多个钩子函数,我们可以利用这些钩子函数在指令的不同生命周期阶段执行特定的逻辑:

  • bind: 只调用一次,指令第一次绑定到元素时调用。可以在这里进行一次性的初始化设置。
  • inserted: 当绑定元素插入到 DOM 中时调用。
  • update: 所在组件的 VNode 更新时调用,但不保证其子 VNode 也更新。
  • componentUpdated: 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  • unbind: 只调用一次,指令与元素解绑时调用。可以在这里进行清理操作。

5. 尽量保持指令的职责单一

每个自定义指令应该尽量做一件事,并且做得好。职责单一的指令更容易维护和调试。例如,一个指令负责拖拽,一个指令负责改变颜色,而不是把两者混合在一起。

// 拖拽指令
Vue.directive('drag', {// ...
});// 改变颜色指令
Vue.directive('change-color', {// ...
});

6. 避免在指令中直接操作数据

尽量避免在自定义指令中直接操作 Vue 实例的数据,保持指令的通用性。指令的主要职责是操作 DOM,数据逻辑应该放在组件中处理。

7. 使用 vnode.context 访问组件实例

在自定义指令中,vnode.context 提供了对 Vue 组件实例的访问。这在某些情况下非常有用,例如需要调用组件的方法。

Vue.directive('example', {bind: function (el, binding, vnode) {// 访问组件实例const vm = vnode.context;vm.someMethod();}
});

8. 使用可选参数和修饰符

利用可选参数和修饰符,可以让自定义指令更加灵活和强大。例如,下面的 v-resize 指令可以接受一个参数来指定方向(水平或垂直):

Vue.directive('resize', {bind: function (el, binding) {const direction = binding.arg || 'both';el.style.resize = direction;}
});

使用时:

<textarea v-resize:horizontal></textarea>

9. 提供默认值

在指令的实现中,可以为传递的参数提供默认值,以提高指令的鲁棒性:

Vue.directive('tooltip', {bind: function (el, binding) {const options = binding.value || {};const position = options.position || 'top';const color = options.color || 'black';// 创建并插入 tooltip 元素...}
});

使用时:

<p v-tooltip="{ position: 'bottom', color: 'blue' }">Hover me</p>

10. 使用传递对象简化参数

通过传递对象,可以更方便地传递多个参数,并且让代码更具可读性:

Vue.directive('highlight', {bind: function (el, binding) {const { color, backgroundColor, fontSize } = binding.value;el.style.color = color || 'black';el.style.backgroundColor = backgroundColor || 'white';el.style.fontSize = fontSize || '16px';}
});

使用时:

<p v-highlight="{ color: 'red', backgroundColor: 'yellow', fontSize: '20px' }">Highlighted Text</p>

11. 考虑性能问题

在实现复杂的自定义指令时,注意性能问题。例如,在 update 钩子函数中避免进行复杂的计算或频繁的 DOM 操作。可以使用防抖或节流技术来优化性能。

Vue.directive('debounce-click', {bind: function (el, binding) {let timeout;el.addEventListener('click', () => {clearTimeout(timeout);timeout = setTimeout(() => {binding.value();}, binding.arg || 300);});},unbind: function (el) {clearTimeout(el.__timeout__);}
});

总结

自定义指令是 Vue.js 提供的一个非常强大的功能,它让我们可以对 DOM 元素进行底层操作,并将这些操作封装成指令以便在模板中重复使用。
自定义指令不仅增强了 Vue 的灵活性,也为我们提供了操作 DOM 的强大工具。掌握了这些知识,能让你在实际项目中更好地实现各种复杂的交互需求。

相关文章:

Vue 自定义指令 Directive 的高级使用与最佳实践

前言 Vue.js 是一个非常流行的前端框架&#xff0c;它的核心理念是通过声明式的方式来描述 UI 和数据绑定。除了模板语法和组件系统&#xff0c;Vue 还提供了一个强大的功能——自定义指令。 自定义指令可以让我们对 DOM 元素进行底层操作&#xff0c;下面让我们通过一个有趣的…...

万字图文实战:从0到1构建 UniApp + Vue3 + TypeScript 移动端跨平台开源脚手架

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f343; vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f…...

在WebStorm遇到Error: error:0308010C:digital envelope routines::unsupported报错时的解决方案

作者&#xff1a;CSDN-PleaSure乐事 欢迎大家阅读我的博客 希望大家喜欢 使用环境&#xff1a;WebStorm 目录 介绍 解决 分析 方法一&#xff1a;设置环境变量 使用WebStorm 使用其他编译器 方法二&#xff1a;使用nvm切换nodejs版本 方法三&#xff1a;更新依赖版本 介…...

数据库产品中SQL注入防护功能应该包含哪些功能

数据库产品中 SQL 注入防护功能应包含以下几方面&#xff1a; 输入验证与过滤功能&#xff1a; 数据类型和格式验证&#xff1a;检查用户输入的数据是否符合预期的数据类型&#xff0c;比如对于一个应该是整数类型的字段&#xff0c;检查输入是否为整数&#xff1b;对于字符串…...

Ribbon客户端负载均衡策略测试及其改进

文章目录 一、目的概述二、验证步骤1、源码下载2、导入IDE3、运行前修改配置4、策略说明5、修改策略 三、最终结论四、改进措施1. 思路分析2. 核心代码3. 测试页面 一、目的概述 为了验证Ribbon客户端负载均衡策略在负载节点失效的情况下&#xff0c;是否具有故障转移的功能&a…...

linux网络编程5——Posix API和网络协议栈,使用TCP实现P2P通信

文章目录 Posix API和网络协议栈&#xff0c;使用TCP实现P2P通信1. socket()2. bind()3. listen()4. connect()5. accept()6. read()/write(), recv()/send()7. 内核tcp数据传输7.1 TCP流量控制7.2 TCP拥塞控制——慢启动/拥塞避免/快速恢复/快速重传 8. shutdown()9. close()9…...

低代码平台中的功能驱动开发:模块化与领域设计

在现代软件开发中&#xff0c;尤其是在低代码平台的背景下&#xff0c;清晰地定义功能和模块是成功的关键。功能驱动开发强调功能的优先性&#xff0c;模块化设计则确保系统的可维护性和可扩展性。本文将探讨如何在低代码平台中有效地将功能与模块结合起来&#xff0c;形成一个…...

HTTP和HTTPS基本概念,主要区别,应用场景

HTTP和 HTTPS是用于在网络中传输数据的协议&#xff0c;虽然它们的功能类似&#xff0c;但在安全性上存在显著差异。 1. HTTP 的基本概念 定义&#xff1a;HTTP 是一种无状态的、面向请求-响应的协议&#xff0c;用于客户端&#xff08;如浏览器&#xff09;和服务器之间传输…...

node.js使用Sequelize ORM操作数据库

一、什么是ORM ORM是在数据库和编程语言之间建立一种映射关系&#xff0c;这样可以让我们有非常简单的代码&#xff0c;来实现各种数据库的操作。 例如&#xff1a;使用mysql去查找表&#xff08;表名称为Articles&#xff09; SELECT * FROM Articles;但是我们使用ORM的话&…...

STM32-Modbus协议(一文通)

Modbus协议原理 RT-Thread官网开源modbus RT-Thread官方提供 FreeModbus开源。 野火有移植的例程。 QT经常用 libModbus库。 Modbus是什么&#xff1f; Modbus协议&#xff0c;从字面理解它包括Mod和Bus两部分&#xff0c;首先它是一种bus&#xff0c;即总线协议&#xff0c;和…...

100. 不同方向的投影视图

本节课给大家讲解&#xff0c;通过UI按钮界面交互改变threejs相机的观察视角。 x轴方向观察 // 通过UI按钮改变相机观察角度 document.getElementById(x).addEventListener(click, function () {camera.position.set(500, 0, 0); //x轴方向观察camera.lookAt(0, 0, 0); //重新…...

Appium中的api(三)

目录 Appium中的api(三) 1.输入和清空内容 1--输入内容 2--清空内容 2.获取文本内容 3.获取文本位置 4.获取文本的大小&#xff08;即获取控件的宽和高&#xff09; 5.滑动api 6.拖拽api 7.如何获取手机分辨率 8.如何截图 9.模拟按键事件api 10.操作通知栏 案例:App自动化模拟 …...

踩坑:关于使用ceph pg repair引发的业务阻塞

概述 在某次故障回溯中&#xff0c;发现引发集群故障&#xff0c;slow io&#xff0c;pg stuck的罪魁祸首竟是做了一次ceph pg repair $pgid。然而ceph pg repair作为使用频率极高的&#xff0c;用来修复pg不一致的常用手段&#xff0c;平时可能很少注意其使用规范和可能带来的…...

瞬间升级!电子文档华丽变身在线题库,效率翻倍✨

&#x1f44b;嘿小伙伴们&#xff0c;有个超赞的秘籍要告诉你们——土著刷题能将你的电子文档一键变身在线题库&#xff01;&#x1f609; 你还没发现这个宝藏功能吗&#xff1f;快来瞧瞧&#xff01; &#x1f31f;是不是常被一堆电子版的学习资料搞得头昏脑涨&#xff0c;学习…...

如何动态改变本地的ip

在当今数字化时代&#xff0c;网络连接已成为我们日常生活和工作中不可或缺的一部分。无论是出于隐私保护、突破地域限制&#xff0c;还是为了测试和优化网络应用&#xff0c;动态改变本地IP地址的需求日益增多。本文将详细介绍如何安全、有效地实现这一目标&#xff0c;旨在帮…...

Spring Boot框架在中小企业设备管理中的创新应用

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…...

Ceph入门到精通-Osd db扩容

ceph-bluestore-tool 是一个在 BlueStore 实例上执行低级管理操作的实用程序。 以下命令可用于 ceph-bluestore-tool 语法 ceph-bluestore-tool COMMAND [ --dev DEVICE … ] [ -i OSD_ID ] [ --path OSD_PATH ] [ --out-dir DIR ] [ --log-file | -l filename ] [ --deep ]c…...

windows msvc2017 x64编译AWS SDK CPP库

在本文中&#xff0c;我们将介绍如何编译AWS SDK C库&#xff0c;以便在您的项目中使用。AWS SDK C库提供了与Amazon Web Services交互的接口&#xff0c;允许您在C应用程序中使用AWS服务。 一、准备工作 在开始编译AWS SDK C库之前&#xff0c;请确保您的系统已经安装了以下…...

铜业机器人剥片 - SNK施努卡

SNK施努卡有色行业电解车间铜业机器人剥片 铜业机器人剥片技术是针对传统人工剥片效率低下、工作环境恶劣及生产质量不稳定的痛点而发展起来的自动化解决方案。 面临人工剥片的诸多挑战&#xff0c;包括低效率、工作环境差、人员流动大以及产品质量控制不精确等问题。 人工剥片…...

非接触式竖向位移、水平位移视频实时在线监测的设备分类及选型

前言 视觉是人工智能正在快速发展的一个分支&#xff0c;简单说来&#xff0c;机器视觉就是用机器代替人眼来做测量和判断。在结构健康自动化监测方面&#xff0c;机器视觉采用光学图像结合智能算法和物联网技术&#xff0c;利用先进的智能靶标识别及亚像素处理等技术&#xff…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!

5月28日&#xff0c;中天合创屋面分布式光伏发电项目顺利并网发电&#xff0c;该项目位于内蒙古自治区鄂尔多斯市乌审旗&#xff0c;项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站&#xff0c;总装机容量为9.96MWp。 项目投运后&#xff0c;每年可节约标煤3670…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

Rust 异步编程

Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...