vue-model如何自定义指令,及批量注册自定义指令

一、在Vue.js中,v-model是一个用于在表单输入和应用状态之间创建双向绑定的指令。要编写自定义的v-model指令,你需要使用Vue的自定义指令API。以下是编写自定义v-model指令的步骤:

  1. 定义一个自定义指令对象。
  2. 在指令对象的bind钩子函数中,设置元素的初始值。
  3. inserted钩子函数中,添加事件监听器来更新数据。
  4. componentUpdated钩子函数中,确保当父组件的数据变化时,更新元素的值。
  5. unbind钩子函数中,移除事件监听器。

下面是一个示例代码,展示了如何创建一个自定义的v-model指令:

Vue.directive('custom-model', {bind: function(el, binding, vnode) {// 初始化元素值el.value = binding.value;// 定义一个处理函数,用于更新数据function handleInput(event) {const newValue = event.target.value;if (newValue !== binding.value) {vnode.context.$emit('input', newValue);}}// 为元素添加事件监听器el.addEventListener('input', handleInput);// 将处理函数存储在元素的_handleInput属性中,以便后续可以移除监听器el._handleInput = handleInput;},inserted: function(el, binding, vnode) {// 可以在这里执行一些额外的操作,例如聚焦输入框等},update: function(el, binding, vnode) {// 确保元素的值与绑定的值同步if (binding.value !== el.value) {el.value = binding.value;}},unbind: function(el) {// 移除事件监听器el.removeEventListener('input', el._handleInput);delete el._handleInput;}
});

在这个示例中,我们创建了一个名为custom-model的自定义指令。这个指令会在绑定的元素上设置一个输入事件监听器,当用户输入时,它会触发一个input事件并更新绑定的数据。同时,当绑定的数据发生变化时,指令的update钩子会确保元素的值也相应地更新。最后,当指令解绑时,我们会移除事件监听器以清理资源。

二、如何批量注册v-model自定义指令

在Vue.js中,如果你需要批量注册多个自定义指令,包括v-model指令,你可以使用一个循环来简化这个过程。以下是一个示例,展示了如何批量注册多个自定义指令,包括自定义的v-model指令:

// 定义自定义指令对象
const customDirectives = {'custom-model': {bind: function(el, binding, vnode) {el.value = binding.value;function handleInput(event) {const newValue = event.target.value;if (newValue !== binding.value) {vnode.context.$emit('input', newValue);}}el.addEventListener('input', handleInput);el._handleInput = handleInput;},inserted: function(el, binding, vnode) {// 可以在这里执行一些额外的操作,例如聚焦输入框等},update: function(el, binding, vnode) {if (binding.value !== el.value) {el.value = binding.value;}},unbind: function(el) {el.removeEventListener('input', el._handleInput);delete el._handleInput;}},// 其他自定义指令可以在这里添加
};// 批量注册自定义指令
Object.keys(customDirectives).forEach(key => {Vue.directive(key, customDirectives[key]);
});

在这个示例中,我们首先定义了一个包含所有自定义指令的对象customDirectives。然后,我们使用Object.keys()方法获取这个对象的所有键(即指令名称),并使用forEach循环遍历这些键,调用Vue.directive()方法来注册每个自定义指令。

这样,你就可以轻松地批量注册多个自定义指令,包括自定义的v-model指令。

三、常用自定义指令

1、v-copy

需求:实现一键复制文本内容,用于鼠标右键黏贴

思路:

  1. 创建自定义指令:在Vue实例中定义一个自定义指令 v-copy
  2. 使用Clipboard API:利用现代浏览器提供的 navigator.clipboard.writeText() 方法来实现复制功能。
  3. 处理右键事件:监听元素的右键事件,并触发复制操作。
  4. 回退机制:如果 navigator.clipboard 不可用,可以使用传统的DOM操作方式作为回退方案。

实现代码如下:

// main.js or where you define your Vue instance
import Vue from 'vue';Vue.directive('copy', {bind(el, binding) {const textToCopy = binding.value;// Function to handle the copy actionfunction handleCopy() {if (navigator.clipboard && window.isSecureContext) {navigator.clipboard.writeText(textToCopy).then(() => {console.log('复制成功!');}).catch((err) => {console.error('复制失败:', err);fallbackCopyTextToClipboard(textToCopy);});} else {fallbackCopyTextToClipboard(textToCopy);}}// Fallback method for older browsersfunction fallbackCopyTextToClipboard(text) {const textArea = document.createElement('textarea');textArea.value = text;textArea.style.position = 'fixed'; // Prevent scrolling to bottom of page in MS Edge.document.body.appendChild(textArea);textArea.focus();textArea.select();try {document.execCommand('copy');console.log('复制成功!');} catch (err) {console.error('复制失败:', err);}document.body.removeChild(textArea);}// Add event listener for right-click context menuel.addEventListener('contextmenu', (event) => {event.preventDefault(); // Prevent default context menuhandleCopy();});}
});// In your Vue component template
<template><div v-copy="'要复制的文本内容'">点击右键复制这段文本</div>
</template>
  1. 自定义指令绑定:在 bind 钩子中,我们获取到需要复制的文本内容 binding.value
  2. 处理复制逻辑
    • 如果浏览器支持 navigator.clipboard 并且当前页面是在安全上下文(HTTPS)下运行,则使用 navigator.clipboard.writeText 方法进行复制。
    • 如果不支持或发生错误,则调用 fallbackCopyTextToClipboard 函数,该函数使用传统的方法创建一个隐藏的 <textarea> 元素来执行复制操作。
  3. 右键事件监听:通过监听 contextmenu 事件,阻止默认的右键菜单弹出,并调用 handleCopy 函数执行复制操作。
  4. 回退机制:在 fallbackCopyTextToClipboard 函数中,创建一个隐藏的 <textarea> 元素,将文本内容放入其中,选中并执行复制命令,最后移除该元素。

这样,你就可以在Vue组件中使用 v-copy 自定义指令,实现一键复制文本内容的功能,并在鼠标右键时触发复制操作。

2、拖拽自定义指令

需求:实现一个拖拽指令,可在页面可视区域任意拖拽元素

export default {inserted(el) {let disX, disY;const oDiv = el;const onMouseDown = (e) => {// 防止默认行为(如选中文本)e.preventDefault();// 计算鼠标相对元素的位置disX = e.clientX - oDiv.offsetLeft;disY = e.clientY - oDiv.offsetTop;// 绑定移动和结束事件document.addEventListener('mousemove', onMouseMove);document.addEventListener('mouseup', onMouseUp);};const onMouseMove = (e) => {// 计算新的位置let left = e.clientX - disX;let top = e.clientY - disY;// 获取视口尺寸const viewportWidth = window.innerWidth || document.documentElement.clientWidth;const viewportHeight = window.innerHeight || document.documentElement.clientHeight;// 获取元素尺寸const elWidth = oDiv.offsetWidth;const elHeight = oDiv.offsetHeight;// 限制元素不超出视口left = Math.max(0, Math.min(left, viewportWidth - elWidth));top = Math.max(0, Math.min(top, viewportHeight - elHeight));// 设置元素位置oDiv.style.left = `${left}px`;oDiv.style.top = `${top}px`;};const onMouseUp = () => {// 移除事件监听器document.removeEventListener('mousemove', onMouseMove);document.removeEventListener('mouseup', onMouseUp);};// 绑定鼠标按下事件oDiv.addEventListener('mousedown', onMouseDown);// 可选:添加触摸事件支持const onTouchStart = (e) => {if (e.touches.length !== 1) return; // 只处理单点触控const touch = e.touches[0];disX = touch.clientX - oDiv.offsetLeft;disY = touch.clientY - oDiv.offsetTop;document.addEventListener('touchmove', onTouchMove);document.addEventListener('touchend', onTouchEnd);};const onTouchMove = (e) => {if (e.touches.length !== 1) return;const touch = e.touches[0];let left = touch.clientX - disX;let top = touch.clientY - disY;const viewportWidth = window.innerWidth || document.documentElement.clientWidth;const viewportHeight = window.innerHeight || document.documentElement.clientHeight;const elWidth = oDiv.offsetWidth;const elHeight = oDiv.offsetHeight;left = Math.max(0, Math.min(left, viewportWidth - elWidth));top = Math.max(0, Math.min(top, viewportHeight - elHeight));oDiv.style.left = `${left}px`;oDiv.style.top = `${top}px`;};const onTouchEnd = () => {document.removeEventListener('touchmove', onTouchMove);document.removeEventListener('touchend', onTouchEnd);};oDiv.addEventListener('touchstart', onTouchStart);},unbind(el) {// 移除所有事件监听器el.removeEventListener('mousedown', this.onMouseDown);el.removeEventListener('touchstart', this.onTouchStart);document.removeEventListener('mousemove', this.onMouseMove);document.removeEventListener('mouseup', this.onMouseUp);document.removeEventListener('touchmove', this.onTouchMove);document.removeEventListener('touchend', this.onTouchEnd);}
};
  1. 事件绑定和解绑:

    • 将 mousedownmousemovemouseuptouchstarttouchmove 和 touchend 事件绑定到目标元素本身,而不是全局 document。这避免了多个可拖拽元素之间的事件冲突,并提高了性能。
    • 在 unbind 钩子中,确保移除所有添加的事件监听器,防止内存泄漏。
  2. 边界检查:

    • 在 onMouseMove 和 onTouchMove 函数中,计算元素的新位置时,加入了对视口尺寸的检查,确保元素不会拖出视窗范围。
  3. 触摸设备支持:

    • 添加了对触摸事件的处理,使指令在移动设备上也能正常工作。
  4. 代码现代化:

    • 使用了 ES6+ 的箭头函数和模板字符串,使代码更加简洁易读。
    • 使用 addEventListener 和 removeEventListener 代替直接赋值事件处理器,增强了代码的可维护性和可读性。
  5. 防止默认行为:

    • 在 onMouseDown 中调用 e.preventDefault(),防止在拖拽过程中出现意外的默认行为(如文本选择)。
  6. 性能优化:

    • 通过将事件监听器绑定到具体元素,减少了不必要的事件冒泡和捕获,提升了性能。
  7. 样式控制:

    • 通过计算并设置 left 和 top 样式,而不是直接修改 style 属性,避免了与其他 CSS 规则的潜在冲突。使用示例
    • <template><div v-drags class="draggable">拖拽我</div>
      </template><script>
      import drags from '@/directive/drag'; // 根据实际路径调整export default {directives: {drags}
      };
      </script><style scoped>
      .draggable {position: absolute; /* 确保元素是绝对定位 */width: 100px;height: 100px;background-color: lightblue;cursor: grab;
      }
      </style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/70192.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单认识一下-Redis

一、什么是Redis Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的、基于内存的数据结构存储系统&#xff0c;它既可以用作数据库、缓存&#xff0c;也可以作为消息中间件使用。以下为你详细介绍 Redis&#xff1a; 基本特点 高性能&#xff1a;Redis 将数…

LabVIEW的吞雨测控系统

本案例介绍了一种基于LabVIEW开发的吞雨测控系统&#xff0c;该系统通过建模仿真分析不同控制器模式下的阶跃信号响应&#xff0c;从而选择了最适合的控制器。为了有效解决在控制流量过程中出现的振荡收敛和流量信号大扰动问题&#xff0c;系统采用了改进的积分分离PID算法&…

C++中的顺序容器(一)

文章目录 顺序容器概述所有容器类型都支持的操作迭代器容器定义与初始化将一个容器初始化为另一个容器的拷贝标准库array具有固定大小 赋值和swap关系运算符 顺序容器的特有操作向顺序容器添加元素访问元素删除元素特殊的forward_list操作改变容器的大小容器操作可能是迭代器失…

Javaweb中,使用Servlet编写简单的接口

案例&#xff1a;网页提交用户名和密码信息&#xff0c;后端校验密码长度需在6-12位之间 后端部分 WebServlet("/valid") public class SimpleServlet extends HttpServlet{public void service(HttpServletRequest req, HttpServletResponse resp) throws IOExcepti…

C语言实现的常见排序算法

排序是计算机科学中非常重要的基础算法之一。无论是在数据分析、数据库查询还是图形界面中&#xff0c;我们都可能会遇到排序问题。本文将介绍几种常见的排序算法&#xff0c;并提供其C语言实现代码。排序算法的效率和应用场景有很大关系&#xff0c;不同的算法有不同的时间复杂…

对于简单的HTML、CSS、JavaScript前端,我们可以通过几种方式连接后端

1. 使用Fetch API发送HTTP请求&#xff08;最简单的方式&#xff09;&#xff1a; //home.html // 示例&#xff1a;提交表单数据到后端 const submitForm async (formData) > {try {const response await fetch(http://your-backend-url/api/submit, {method: POST,head…

[论文阅读] SeeSR: Towards Semantics-Aware Real-World Image Super-Resolution

文章目录 一、前言二、主要贡献三、Introduction四、Methodology4.1 Motivation &#xff1a;4.2Framework Overview.** 一、前言 通信作者是香港理工大学 & OPPO研究所的张磊教授&#xff0c;也是图像超分ISR的一个大牛了。 论文如下 SeeSR: Towards Semantics-Aware Rea…

案例-04.部门管理-删除

一.功能演示 二.需求说明 三.接口文档 四.思路 既然是通过id删除对应的部门&#xff0c;那么必然要获取到前端请求的要删除部门的id。id作为请求路径传递过来&#xff0c;那么要从请求路径中获取&#xff0c;id是一个路径参数。因此使用注解PathVariable获取路径参数。 请求方…

Blazor-父子组件传递任意参数

在我们从父组件传参数给子组件时&#xff0c;可以通过子组件定义的[Parameter]特性的公开属性进行传值&#xff0c;但是当我们需要传递多个值的时候&#xff0c;就需要通过[Parameter]特性定义多个属性&#xff0c;有没有更简便的方式&#xff1f; 我们可以使用定义 IDictionar…

DeepSeek 的创新融合:多行业应用实践探索

引言 在数字化转型的浪潮中&#xff0c;技术的融合与创新成为推动各行业发展的关键力量。蓝耘平台作为行业内备受瞩目的创新平台&#xff0c;以其强大的资源整合能力和灵活的架构&#xff0c;为企业提供了高效的服务支持。而 DeepSeek 凭借先进的人工智能技术&#xff0c;在自然…

STM32创建静态库lib

创建静态库lib 1. 新建工程1.1 创建工程文件夹1.2 编写用户相关代码1.2.1 stm32f4xx_it.h1.2.2 stm32f4xx_it.c1.2.3 标准库配置&#xff1a;stm32f4xx_conf.h1.2.4 HAL库的配置&#xff1a;stm32f4xx_hal_conf.h1.2.5 LL库配置&#xff1a;stm32f4xx_ll_conf.h 1.3 移植通用文…

elabradio入门第二讲——BPSK数字调制与解调(插值、升余弦滤波、速率匹配、符号同步)

数字信号可以通过数字基带传输系统进行传输&#xff0c;而基带传输系统仅仅适用于低频信道下的数字信号传输。然而&#xff0c;在实际的通信系统中信道通常具有带通特性&#xff0c;因而需要将基带信号搬移到适合信道传输的高频载波上&#xff0c;使得信号与信道相匹配&#xf…

汽车 OTA 升级:提升下载与升级速度,优化用户体验

摘要&#xff1a; 随着汽车智能化的飞速发展&#xff0c;OTA&#xff08;Over - the - Air&#xff09;升级已成为汽车行业的重要技术&#xff0c;它能为车辆持续带来功能更新与性能优化。然而&#xff0c;下载及升级速度较慢的问题常常影响用户体验。本文深入探讨在汽车 OTA …

【Spring+MyBatis】留言墙的实现

目录 1. 添加依赖 2. 配置数据库 2.1 创建数据库与数据表 2.2 创建与数据库对应的实体类 3. 后端代码 3.1 目录结构 3.2 MessageController类 3.3 MessageService类 3.4 MessageMapper接口 4. 前端代码 5. 单元测试 5.1 后端接口测试 5.2 使用前端页面测试 在Spri…

SQLite Select 语句详解

SQLite Select 语句详解 SQLite 是一个轻量级的数据库管理系统&#xff0c;以其简洁的设计和高效的性能被广泛应用于各种场景。在 SQLite 中&#xff0c;SELECT 语句是用于查询数据库中的数据的命令。本文将详细介绍 SQLite 的 SELECT 语句&#xff0c;包括其基本语法、常用功…

深度学习05 ResNet残差网络

目录 传统卷积神经网络存在的问题 如何解决 批量归一化BatchNormalization, BN 残差连接方式 ​残差结构 ResNet网络 ResNet 网络是在 2015年 由微软实验室中的何凯明等几位大神提出&#xff0c;斩获当年ImageNet竞赛中分类任务第一名&#xff0c;目标检测第一名。获得CO…

组件库地址

react&#xff1a; https://react-vant.3lang.dev/components/dialoghttps://react-vant.3lang.dev/components/dialog vue用v2的 Vant 2 - Mobile UI Components built on Vue

docker 进阶命令(基于Ubuntu)

数据卷 Volume: 目录映射, 目录挂载 匿名绑定: 匿名绑定的 volume 在容器删除的时候, 数据卷也会被删除, 匿名绑定是不能做到持久化的, 地址一般是 /var/lib/docker/volumes/xxxxx/_data 绑定卷时修改宿主机的目录或文件, 容器内的数据也会同步修改, 反之亦然 # 查看所有 vo…

从入门到精通:Postman 实用指南

Postman 是一款超棒的 API 开发工具&#xff0c;能用来测试、调试和管理 API&#xff0c;大大提升开发效率。下面就给大家详细讲讲它的安装、使用方法&#xff0c;再分享些实用技巧。 一、安装 Postman 你能在 Postman 官网&#xff08;https://www.postman.com &#xff09;下…

将图片base64编码后,数据转成图片

将图片数据进行base64编码后&#xff0c;可以在浏览器上查看图片&#xff0c;只需在前端加上data:image/png;base64,即可 在线工具&#xff1a; Base64转图片 - 加菲工具