vue项目里很实用的全局Vue自定义指令

news/2024/3/29 23:16:10/文章来源:https://blog.csdn.net/qq_37635012/article/details/128958135

在 Vue,除了核心功能默认内置的指令 ( v-model 和 v-show ),Vue 也允许注册自定义指令。它的作用价值在于当开发人员在某些场景下需要对普通 DOM 元素进行操作。
Vue 自定义指令有全局注册和局部注册两种方式。先来看看注册全局指令的方式,通过 Vue.directive( id, [definition] ) 方式注册全局指令。然后在入口文件中进行 Vue.use() 调用。

全局注册自定义指令

新建 directives/index.js 文件

import copy from './copy'
import longpress from './longpress'
// 自定义指令
const directives = {copy,longpress,
}export default {install(Vue) {Object.keys(directives).forEach((key) => {Vue.directive(key, directives[key])})},
}

在 main.js 引入并调用

import Vue from 'vue'
import Directives from './JS/directives'
Vue.use(Directives)

指令定义函数的钩子函数(可选):

  • bind: 只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作。
  • inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
  • update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值。
  • componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
  • unbind: 只调用一次, 指令与元素解绑时调用。

分享8个常用的vue指令

  • 复制粘贴指令 v-copy
  • 长按指令 v-longpress
  • 输入框防抖指令 v-debounce
  • 禁止表情及特殊字符 v-emoji
  • 图片懒加载 v-LazyLoad
  • 权限校验指令 v-premission
  • 实现页面水印 v-waterMarker
  • 拖拽指令 v-draggable

复制粘贴指令 v-copy
需求:实现一键复制文本内容,用于鼠标右键粘贴。
1.动态创建 textarea 标签,并设置 readOnly 属性及移出可视区域
2.将要复制的值赋给 textarea 标签的 value 属性,并插入到 body
3.选中值 textarea 并复制
4.将 body 中插入的 textarea 移除
5.在第一次调用时绑定事件,在解绑时移除事件

const copy = {bind(el, { value }) {el.$value = valueel.handler = () => {if (!el.$value) {// 值为空的时候,给出提示。可根据项目UI仔细设计console.log('无复制内容')return}// 动态创建 textarea 标签const textarea = document.createElement('textarea')// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域textarea.readOnly = 'readonly'textarea.style.position = 'absolute'textarea.style.left = '-9999px'// 将要 copy 的值赋给 textarea 标签的 value 属性textarea.value = el.$value// 将 textarea 插入到 body 中document.body.appendChild(textarea)// 选中值并复制textarea.select()const result = document.execCommand('Copy')if (result) {console.log('复制成功') // 可根据项目UI仔细设计}document.body.removeChild(textarea)}// 绑定点击事件,就是所谓的一键 copy 啦el.addEventListener('click', el.handler)},// 当传进来的值更新的时候触发componentUpdated(el, { value }) {el.$value = value},// 指令与元素解绑的时候,移除事件绑定unbind(el) {el.removeEventListener('click', el.handler)},
}export default copy

使用:给 Dom 加上 v-copy 及复制的文本即可

<template><button v-copy="copyText">复制</button>
</template>
<script>export default {data() {return {copyText: 'a copy directives',}},}
</script>

长按指令 v-longpress
需求:实现长按,用户需要按下并按住按钮几秒钟,触发相应的事件
思路:

1.创建一个计时器, 2 秒后执行函数
2.当用户按下按钮时触发 mousedown 事件,启动计时器;用户松开按钮时调用mouseout 事件。
3.如果 mouseup 事件 2 秒内被触发,就清除计时器,当作一个普通的点击事件
4.如果计时器没有在 2 秒内清除,则判定为一次长按,可以执行关联的函数。
5.在移动端要考虑 touchstart,touchend 事件
代码实现:

const longpress = {bind: function (el, binding, vNode) {if (typeof binding.value !== 'function') {throw 'callback must be a function'}// 定义变量let pressTimer = null// 创建计时器( 2秒后执行函数 )let start = (e) => {if (e.type === 'click' && e.button !== 0) {return}if (pressTimer === null) {pressTimer = setTimeout(() => {handler()}, 2000)}}// 取消计时器let cancel = (e) => {if (pressTimer !== null) {clearTimeout(pressTimer)pressTimer = null}}// 运行函数const handler = (e) => {binding.value(e)}// 添加事件监听器el.addEventListener('mousedown', start)el.addEventListener('touchstart', start)// 取消计时器el.addEventListener('click', cancel)el.addEventListener('mouseout', cancel)el.addEventListener('touchend', cancel)el.addEventListener('touchcancel', cancel)},// 当传进来的值更新的时候触发componentUpdated(el, { value }) {el.$value = value},// 指令与元素解绑的时候,移除事件绑定unbind(el) {el.removeEventListener('click', el.handler)},
}export default longpress

使用:给 Dom 加上 v-longpress 及回调函数即可

<template><button v-longpress="longpress">长按</button>
</template><script>
export default {methods: {longpress () {alert('长按指令生效')}}
}

防抖指令 v-debounce
需求:防止按钮在短时间内被多次点击,使用防抖函数限制规定时间内只能点击一次。
思路:
1.定义一个延迟执行的方法,如果在延迟时间内再调用该方法,则重新计算执行时间。
2.将时间绑定在 click 方法上。

const debounce = {inserted: function (el, binding) {let timerel.addEventListener('click', () => {if (timer) {clearTimeout(timer)}timer = setTimeout(() => {binding.value()}, 1000)})},
}export default debounce

使用:给 Dom 加上 v-debounce 及回调函数即可

<template><button v-debounce="debounceClick">防抖</button>
</template><script>
export default {methods: {debounceClick () {console.log('只触发一次')}}
}

禁止表情及特殊字符指令 v-emoji
背景:开发中遇到的表单输入,往往会有对输入内容的限制,比如不能输入表情和特殊字符,只能输入数字或字母等。我们常规方法是在每一个表单的 on-change 事件上做处理。

需求:根据正则表达式,设计自定义处理表单输入规则的指令,下面以禁止输入表情和特殊字符为例。

<template><input type="text" v-model="note" @change="vaidateEmoji" />
</template>
<script>export default {methods: {vaidateEmoji() {var reg = /[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/gthis.note = this.note.replace(reg, '')},},}
</script>

这样代码量比较大而且不好维护,所以我们需要自定义一个指令来解决这问题。
实现代码:

let findEle = (parent, type) => {return parent.tagName.toLowerCase() === type ? parent : parent.querySelector(type)
}const trigger = (el, type) => {const e = document.createEvent('HTMLEvents')e.initEvent(type, true, true)el.dispatchEvent(e)
}const emoji = {bind: function (el, binding, vnode) {// 正则规则可根据需求自定义var regRule = /[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/glet $inp = findEle(el, 'input')el.$inp = $inp$inp.handle = function () {let val = $inp.value$inp.value = val.replace(regRule, '')trigger($inp, 'input')}$inp.addEventListener('keyup', $inp.handle)},unbind: function (el) {el.$inp.removeEventListener('keyup', el.$inp.handle)},
}export default emoji

使用:将需要校验的输入框加上 v-emoji 即可

<template><input type="text" v-model="note" v-emoji />
</template>

图片懒加载指令 v-LazyLoad
背景:在类电商类项目,往往存在大量的图片,如 banner 广告图,菜单导航图,美团等商家列表头图等。图片众多以及图片体积过大往往会影响页面加载速度,造成不良的用户体验,所以进行图片懒加载优化势在必行。

需求:实现一个图片懒加载指令,只加载浏览器可见区域的图片。
懒加载思路:
1.图片懒加载的原理主要是判断当前图片是否到了可视区域这一核心逻辑实现的
2.拿到所有的图片 Dom ,遍历每个图片判断当前图片是否到了可视区范围内
3.如果到了就设置图片的 src 属性,否则显示默认图片

图片懒加载有两种方式可以实现,一是绑定 srcoll 事件进行监听,二是使用 IntersectionObserver 判断图片是否到了可视区域,但是有浏览器兼容性问题。

下面封装一个懒加载指令兼容两种方法,判断浏览器是否支持 IntersectionObserver API,如果支持就使用 IntersectionObserver 实现懒加载,否则则使用 srcoll 事件监听 + 节流的方法实现。
实现代码:

const LazyLoad = {// install方法install(Vue, options) {const defaultSrc = options.defaultVue.directive('lazy', {bind(el, binding) {LazyLoad.init(el, binding.value, defaultSrc)},inserted(el) {if (IntersectionObserver) {LazyLoad.observe(el)} else {LazyLoad.listenerScroll(el)}},})},// 初始化init(el, val, def) {el.setAttribute('data-src', val)el.setAttribute('src', def)},// 利用IntersectionObserver监听elobserve(el) {var io = new IntersectionObserver((entries) => {const realSrc = el.dataset.srcif (entries[0].isIntersecting) {if (realSrc) {el.src = realSrcel.removeAttribute('data-src')}}})io.observe(el)},// 监听scroll事件listenerScroll(el) {const handler = LazyLoad.throttle(LazyLoad.load, 300)LazyLoad.load(el)window.addEventListener('scroll', () => {handler(el)})},// 加载真实图片load(el) {const windowHeight = document.documentElement.clientHeightconst elTop = el.getBoundingClientRect().topconst elBtm = el.getBoundingClientRect().bottomconst realSrc = el.dataset.srcif (elTop - windowHeight < 0 && elBtm > 0) {if (realSrc) {el.src = realSrcel.removeAttribute('data-src')}}},// 节流throttle(fn, delay) {let timerlet prevTimereturn function (...args) {const currTime = Date.now()const context = thisif (!prevTime) prevTime = currTimeclearTimeout(timer)if (currTime - prevTime > delay) {prevTime = currTimefn.apply(context, args)clearTimeout(timer)return}timer = setTimeout(function () {prevTime = Date.now()timer = nullfn.apply(context, args)}, delay)}},
}export default LazyLoad

使用:将组件内 标签的 src 换成 v-LazyLoad

<img v-LazyLoad="xxx.jpg" />

权限校验指令 v-premission
背景:在一些后台管理系统,我们可能需要根据用户角色进行一些操作权限的判断,很多时候我们都是粗暴地给一个元素添加 v-if / v-show 来进行显示隐藏,但如果判断条件繁琐且多个地方需要判断,这种方式的代码不仅不优雅而且冗余。针对这种情况,我们可以通过全局自定义指令来处理。

需求:自定义一个权限指令,对需要权限判断的 Dom 进行显示隐藏。
思路:
1.自定义一个权限数组
2.判断用户的权限是否在这个数组内,如果是则显示,否则则移除 Dom

代码实现:

function checkArray(key) {let arr = ['1', '2', '3', '4']let index = arr.indexOf(key)if (index > -1) {return true // 有权限} else {return false // 无权限}
}const permission = {inserted: function (el, binding) {let permission = binding.value // 获取到 v-permission的值if (permission) {let hasPermission = checkArray(permission)if (!hasPermission) {// 没有权限 移除Dom元素el.parentNode && el.parentNode.removeChild(el)}}},
}
export default permission

使用:给 v-permission 赋值判断即可

<div class="btns">//1显示<button v-permission="'1'">权限按钮1</button>//0不显示<button v-permission="'0'">权限按钮2</button>
</div>

实现页面水印指令 v-waterMarker
需求:给整个页面添加背景水印
思路:

1.使用 canvas 特性生成 base64 格式的图片文件,设置其字体大小,颜色等。
2.将其设置为背景图片,从而实现页面或组件水印效果

function addWaterMarker(str, parentNode, font, textColor) {// 水印文字,父元素,字体,文字颜色var can = document.createElement('canvas')parentNode.appendChild(can)can.width = 200can.height = 150can.style.display = 'none'var cans = can.getContext('2d')cans.rotate((-20 * Math.PI) / 180)cans.font = font || '16px Microsoft JhengHei'cans.fillStyle = textColor || 'rgba(180, 180, 180, 0.3)'cans.textAlign = 'left'cans.textBaseline = 'Middle'cans.fillText(str, can.width / 10, can.height / 2)parentNode.style.backgroundImage = 'url(' + can.toDataURL('image/png') + ')'
}const waterMarker = {bind: function (el, binding) {addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor)},
}export default waterMarker

使用:设置水印文案,颜色,字体大小即可

<template><div v-waterMarker="{text:'lzg版权所有',textColor:'rgba(180, 180, 180, 0.4)'}"></div>
</template>

拖拽指令 v-draggable
需求:实现一个拖拽指令,可在页面可视区域任意拖拽元素。
思路:
1.设置需要拖拽的元素为相对定位,其父元素为绝对定位。
2.鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。
3.鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值
4.鼠标松开(onmouseup)时完成一次拖拽

const draggable = {inserted: function (el) {el.style.cursor = 'move'el.onmousedown = function (e) {let disx = e.pageX - el.offsetLeftlet disy = e.pageY - el.offsetTopdocument.onmousemove = function (e) {let x = e.pageX - disxlet y = e.pageY - disylet maxX = document.body.clientWidth - parseInt(window.getComputedStyle(el).width)let maxY = document.body.clientHeight - parseInt(window.getComputedStyle(el).height)if (x < 0) {x = 0} else if (x > maxX) {x = maxX}if (y < 0) {y = 0} else if (y > maxY) {y = maxY}el.style.left = x + 'px'el.style.top = y + 'px'}document.onmouseup = function () {document.onmousemove = document.onmouseup = null}}},
}
export default draggable

使用: 在 Dom 上加上 v-draggable 即可

<template><div class="el-dialog" v-draggable></div>
</template>

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

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

相关文章

全国青少年编程等级考试scratch四级真题2022年9月(含题库答题软件账号)

青少年编程等级考试scratch真题答题考试系统请点击电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手1、运行下列程序&#xff0c;说法正确的是&#xff1f;&#xff08; &#xff09;A.列表…

计算机组成原理第七章笔记记录

仅仅作为笔记记录,B站视频链接&#xff0c;若有错误请指出&#xff0c;谢谢 基本概念 演变过程 I/O系统基本组成 I/O软件 包括驱动程序、用户程序、管理程序、升级补丁等 下面的两种方式是用来实现CPU和I/O设备的信息交换的 I/O指令 CPU指令的一部分,由操作码,命令码,设备…

【电商】订单系统--售后的简易流程与系统关系

用户进行了订单签收并不意味着终结&#xff0c;这只是一个新的开始&#xff0c;因为商品送达后可能会由于运输过程包装或商品有破损&#xff0c;商品本质量并非商品详情中所描述的那样等各种原因使用户进行退货或换货&#xff1b;还有一种场景是用户签收后发现有的商品漏发、少…

网络通讯的理解

tcp/ip 协议族ip在真实环境中&#xff0c;会把主机号再分成一个子网号和一个主机号。这样的主机号才是最终容纳的主机数量。所以需要使用子网掩码&#xff08;32位&#xff09;来分子网号和主机号。其中值为1的比特是网络号和子网号&#xff0c;值为0的是比特是主机号。可以在w…

chatGPT 官网使用详细教程 (亲测可行)

文章目录1. chatGPT 介绍2. 进入官网3. 开始使用1. chatGPT 介绍 chatGPT 是一款由 OpenAI 开发的聊天机器人模型&#xff0c;它能够模拟人类的语言行为&#xff0c;与用户进行自然的交互。它的名称来源于它所使用的技术—— GPT-3架构&#xff0c;即生成式语言模型的第3代。 …

软件测试】测试时间不够了,我很慌?项目马上发布了......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 常见的几种情况&…

Java开发学习(四十六)----MyBatisPlus新增语句之id生成策略控制及其简化配置

在前面有一篇博客&#xff1a;Java开发学习(四十一)----MyBatisPlus标准数据层&#xff08;增删查改分页&#xff09;开发&#xff0c;我们在新增的时候留了一个问题&#xff0c;就是新增成功后&#xff0c;主键ID是一个很长串的内容。 我们更想要的是按照数据库表字段进行自增…

知乎kol投放怎么做?知乎kol资源从哪里找?

每个领域都有一些比较专业且具有话语权的大V博主&#xff0c;他们推荐某个产品或是品牌就能对粉丝产生很深的影响力&#xff0c;影响用户消费决策。 互联网时代&#xff0c;每个热门的内容平台上都活跃着一大批kol博主&#xff0c;做kol投放具有很高的商业价值。 知乎内容社区…

舆情监测方案怎么写,TOOM舆情监测系统解决方案

舆情监测是通过网络和媒体来收集、分析、评估和报告关于某一特定话题或组织的舆论动态的过程。舆情监测方案通常包括数据收集、数据分析、报告生成等步骤&#xff0c;以帮助组织了解公众对其的看法和声音&#xff0c;并以此作出相应的决策和行动&#xff0c;舆情监测方案怎么写…

Java开发学习(四十八)----MyBatisPlus删除语句之逻辑删除

1、逻辑删除 接下来要讲解是删除中比较重要的一个操作&#xff0c;逻辑删除&#xff0c;先来分析下问题: 这是一个员工和其所签的合同表&#xff0c;关系是一个员工可以签多个合同&#xff0c;是一个一(员工)对多(合同)的表 员工ID为1的张业绩&#xff0c;总共签了三个合同&a…

linux 安装,卸载jdk8

1>安装1 xshell,xsftp 教育版下载 https://www.xshell.com/zh/free-for-home-school/ 2下载jdk包 https://www.oracle.com/java/technologies/downloads/3在usr下新建java文件夹把jdk包拉进去解压tar -zxvf 4首先使用vim打开etc目录下的profile文件 --> vim /etc/profile…

餐饮企业数据可视化大屏(智慧餐饮)

随着信息技术的深入发展&#xff0c;数据大屏的适用场景日益广泛&#xff0c;集工作汇报、实时监控和预测分析等功能于一身。 数据可视化的本质是视觉对话&#xff0c;数据可视化将数据分析技术与图形技术结合&#xff0c;清晰有效地将分析结果信息进行解读和传达。 当前很多餐…

Lecture3 梯度下降(Gradient Descent)

目录 1 问题背景 2 批量梯度下降 (Batch Gradient Descent) 3 鞍点(Saddle Point) 3 随机梯度下降 (Stochastic Gradient Descent) 4 小批量梯度下降 (Mini-batch Gradient Descent) 1 问题背景 图1 上节课讲述的穷举法求最优权重值在Lecture2中&#xff0c;介绍了使用穷举…

重磅!微软推出首款 ChatGPT 版搜索引擎!

微软近期推出了首款 ChatGPT 版搜索引擎&#xff0c;今天带大家一起来看一下。 一夜之间&#xff0c;全球最大的科技公司仿佛都回到了自己年轻时的样子。 在谷歌宣布「实验性对话式人工智能服务」Bard 之后仅 24 小时&#xff0c;北京时间 2 月 8 日凌晨两点&#xff0c;微软发…

2023 软件测试行业内卷动荡,红利期过去后,何去何从?

前段时间席卷全互联网行业的内卷现象&#xff0c;想必有不少人都深陷其中。其实刚开始测试行业人才往往供不应求&#xff0c;而在发展了十几年后&#xff0c;很多人涌入这个行业开始面对存量竞争。红利期过去了&#xff0c;只剩内部争夺。 即便如此&#xff0c;测试行业仍有许…

微服务 分片 运维管理

微服务 分片 运维管理分片分片的概念分片案例环境搭建案例改造成任务分片Dataflow类型调度代码示例运维管理事件追踪运维平台搭建步骤使用步骤分片 分片的概念 当只有一台机器的情况下&#xff0c;给定时任务分片四个&#xff0c;在机器A启动四个线程&#xff0c;分别处理四个…

Python编程自动化办公案例(1)

作者简介&#xff1a;一名在校计算机学生、每天分享Python的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.使用库讲解 1.xlrd 2.xlwt 二.主要案例 1.批量合并 模板如下&#xf…

Monkey

文章目录一、简介二、原理2.1 特殊处理三、命令3.1 启动3.2 关闭四、事件4.1 触摸事件4.2 手势事件4.3 二指缩放事件4.4 轨迹事件4.5 屏幕旋转事件4.6 基本导航事件4.7 主要导航事件4.8 系统按键事件4.9 启动activity事件4.10 键盘事件4.11 其他类型事件五、参数5.1 常规类参数…

go语言实现的一个基于go-zero框架的微服务影院票务系统cinema-ticket

一个基于go-zero框架的微服务影院票务系统cinema-ticket 前言 项目基本介绍 项目开源地址&#xff1a;butane123/cinema-ticket: 一个基于go-zero框架的微服务影院票务系统cinema-ticket (github.com) 这是一个微服务影院票务系统&#xff0c;基于go-zero框架实现&#xff0c…

【Java进阶打卡】JDBC- jdbc连接池

【Java进阶打卡】JDBC- jdbc连接池概述自定义数据库连接池归还连接-装饰设计模式归还连接-适配器设计模式动态代理动态代理-归还数据库连接概述 自定义数据库连接池 DataSource接口概述 javax.sql.DataSource接口&#xff1a;数据源&#xff08;数据库连接池&#xff09; Java…