位置: IT常识 - 正文

Vue自定义指令(含常用8种指令封装)(vue自定义指令生命周期)

编辑:rootadmin
Vue自定义指令(含常用8种指令封装) 基本概念

推荐整理分享Vue自定义指令(含常用8种指令封装)(vue自定义指令生命周期),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:vue自定义指令两种方式,vue自定义指令控制按钮权限,vue自定义指令生命周期,vue自定义指令生命周期,vue自定义指令两种方式,vue自定义指令,vue自定义指令钩子函数,vue自定义指令构造函数,内容如对您有帮助,希望把文章链接给更多的朋友!

一、自定义指令是用来操作dom的,尽管vue推崇数据驱动试图的理念,但并非所又情况都适合数据驱动。自定义指令就是一种有效的补充和扩展,不仅仅可用于定义任何dom操作,并且是可以复用的‘

比如谷歌图片的加载做的非常优雅,在图片未完成加载前,用随机的背景色占位背景图片加载晚成后才直接渲染出来。用自定义只能怪可以非常方便的实现这个功能。

Vue 自定义指令有全局注册和局部注册两种方式。先来看看注册全局指令的方式,通过 Vue.directive( id, [definition] ) 方式注册全局指令。然后在入口文件中进行 Vue.use() 调用。

全局:

比如一个input自动聚焦的例子:在main.js中写入以下内容

// 注册一个全局自定义指令 `v-focus`Vue.directive('focus', {// 当被绑定的元素插入到 DOM 中时……inserted: function (el) {// 聚焦元素el.focus()}})

局部:

directives: {focus: {// 指令的定义inserted: function (el) {el.focus()}}}

使用的时候要在自定义的指令前面加上v-,然后你可以在模板中任何元素上使用新的 v-focus

<input v-focus></input> 钩子函数

指令定义函数提供了几个钩子函数(可选,执行顺序排序)。

bind:只调用一次,指令第一次绑定到元素时调用,可以用这个钩子函数定义一个在绑定时执行一次的初始化动作。

inserted:被绑定的元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。

update:被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。

componentUpdated:被绑定元素所在模板完成一次更新周期时调用。

unbind:只调用一次,指令与元素解绑时调用。

钩子函数的参数主要有以下几项

el:指令所绑定的元素,可以用来直接操作DOM。

binding:一个对象,包含以下属性

name:指令名,不包括v-前缀。

value:指令的绑定值,例如v-my-directive=“1+1”,value的值是2。

oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用,无论值是否改变都可用。

expression:绑定值的表达式或变量名,例如v-my-directive=“1+1”,expression的值是"1+1"。

arg:传给指令的参数,例如v-my-directive:foo,arg的值是"foo"。

modifiers:一个包含修饰符的对象,例如v-my-directive.foo.bar,修饰符对象modifiers的值是{foo:true,bar:true}。

vnode:Vue编译生成的虚拟节点。

oldVnode:上一个虚拟节点,仅在update和componentUpdated钩子中可用。

除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。如以下例子:

<div v-demo="{ color: 'white', text: 'hello!' }"></div><script>Vue.directive('demo', function (el, binding) {console.log(binding.value.color) // "white"console.log(binding.value.text) // "hello!"})</script>

一些自定义指令的应用场景:

防抖、图片懒加载、一键 Copy的功能、拖拽、页面水印、权限校验、输入框自动聚焦、相对时间转换、下拉菜单

批量注册指令

新建 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)下面分享几个实用的 Vue 自定义指令

复制粘贴指令 v-copy

长按指令 v-longpress

输入框防抖指令 v-debounce

禁止表情及特殊字符 v-emoji

图片懒加载 v-LazyLoad

权限校验指令 v-premission

实现页面水印 v-waterMarker

拖拽指令 v-draggable

1、v-copy

需求:

实现一键复制文本内容,用于鼠标右键粘贴。

思路:

动态创建 textarea 标签,并设置 readOnly 属性及移出可视区域

将要复制的值赋给 textarea 标签的 value 属性,并插入到 body

选中值 textarea 并复制

将 body 中插入的 textarea 移除

在第一次调用时绑定事件,在解绑时移除事件

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>2、v-longpress

需求:

实现长按,用户需要按下并按住按钮几秒钟,触发相应的事件

思路:

Vue自定义指令(含常用8种指令封装)(vue自定义指令生命周期)

创建一个计时器, 2 秒后执行函数

当用户按下按钮时触发 mousedown 事件,启动计时器;用户松开按钮时调用 mouseout 事件。

如果 mouseup 事件 2 秒内被触发,就清除计时器,当作一个普通的点击事件

如果计时器没有在 2 秒内清除,则判定为一次长按,可以执行关联的函数。

在移动端要考虑 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('长按指令生效')}}}</script>3、v-debounce

背景:

在开发中,有些提交保存按钮有时候会在短时间内被点击多次,这样就会多次重复请求后端接口,造成数据的混乱,比如新增表单的提交按钮,多次点击就会新增多条重复的数据。

需求:

防止按钮在短时间内被多次点击,使用防抖函数限制规定时间内只能点击一次。

思路:

定义一个延迟执行的方法,如果在延迟时间内再调用该方法,则重新计算执行时间。

将时间绑定在 click 方法上。

const debounce = {inserted: function (el, binding) {let timerel.addEventListener('keyup', () => {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('只触发一次')}}}</script>4、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|rns,.?!,。?!…—&$=()-+/*{}[]]|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|rns,.?!,。?!…—&$=()-+/*{}[]]|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>5、v-LazyLoad

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

需求:实现一个图片懒加载指令,只加载浏览器可见区域的图片。

思路:

图片懒加载的原理主要是判断当前图片是否到了可视区域这一核心逻辑实现的

拿到所有的图片 Dom ,遍历每个图片判断当前图片是否到了可视区范围内

如果到了就设置图片的 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" />6、v-permission

背景:

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

需求:自定义一个权限指令,对需要权限判断的 Dom 进行显示隐藏。

思路:

自定义一个权限数组

判断用户的权限是否在这个数组内,如果是则显示,否则则移除 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 赋值判断即可

<!-- 显示 --><button v-permission="'1'">权限按钮1</button><!-- 不显示 --><button v-permission="'10'">权限按钮2</button></div>7、vue-waterMarker

需求:给整个页面添加背景水印

思路:

使用 canvas 特性生成 base64 格式的图片文件,设置其字体大小,颜色等。

将其设置为背景图片,从而实现页面或组件水印效果

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>8、v-draggable

需求:

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

思路:

设置需要拖拽的元素为相对定位,其父元素为绝对定位。

鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。

鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值

鼠标松开(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>
本文链接地址:https://www.jiuchutong.com/zhishi/298467.html 转载请保留说明!

上一篇:解读YOLO v7的代码(三)损失函数(yolo v5详解)

下一篇:机器学习中的预测评价指标MSE、RMSE、MAE、MAPE、SMAPE

  • 物语,心道(心路物语)

    物语,心道(心路物语)

  • 幻灯片模板设置怎么设置(幻灯片模板设置会影响幻灯片母版的内容)

    幻灯片模板设置怎么设置(幻灯片模板设置会影响幻灯片母版的内容)

  • 拼多多卖家发错货可以要求赔偿吗(拼多多卖家发错信息了怎么撤回信息呢)

    拼多多卖家发错货可以要求赔偿吗(拼多多卖家发错信息了怎么撤回信息呢)

  • vivo x27密码忘了怎么解锁(vivox27密码忘了怎么解锁)

    vivo x27密码忘了怎么解锁(vivox27密码忘了怎么解锁)

  • 充电线变黄了是什么原因(充电线 发黄)

    充电线变黄了是什么原因(充电线 发黄)

  • iPhone白点在哪里设置(苹果白点在哪)

    iPhone白点在哪里设置(苹果白点在哪)

  • 电子dip是什么意思(电子pod)

    电子dip是什么意思(电子pod)

  • ipad2微信哪个版本能用(ipad微信哪个版本好用)

    ipad2微信哪个版本能用(ipad微信哪个版本好用)

  • ipad2微信低版本用不了(ipadmin微信版本低)

    ipad2微信低版本用不了(ipadmin微信版本低)

  • 微信健康码怎么扫不出来(微信健康码怎么查)

    微信健康码怎么扫不出来(微信健康码怎么查)

  • 撕钢化膜会损坏屏幕吗(撕钢化膜会损坏镜头吗)

    撕钢化膜会损坏屏幕吗(撕钢化膜会损坏镜头吗)

  • 手机能否恢复微信聊天记录(怎样用手机恢复微信记录)

    手机能否恢复微信聊天记录(怎样用手机恢复微信记录)

  • 充电线和数据线的区别(充电线和数据线怎么区分)

    充电线和数据线的区别(充电线和数据线怎么区分)

  • 苹果6sp和苹果11哪个屏幕大(苹果6sp和苹果11对比参数)

    苹果6sp和苹果11哪个屏幕大(苹果6sp和苹果11对比参数)

  • 苹果6和6s内存通用吗(6s和6的内存能互换吗)

    苹果6和6s内存通用吗(6s和6的内存能互换吗)

  • w10系统怎么设置中文版(w10系统怎么设置屏幕不黑屏)

    w10系统怎么设置中文版(w10系统怎么设置屏幕不黑屏)

  • 怎么退换货物给卖家(怎么退换货给商家寄回去)

    怎么退换货物给卖家(怎么退换货给商家寄回去)

  • 苹果xr怎么使用无线充电(苹果xr怎么使用双卡双待设置)

    苹果xr怎么使用无线充电(苹果xr怎么使用双卡双待设置)

  • 标书目录算页码吗(标书目录页码错了会废标吗)

    标书目录算页码吗(标书目录页码错了会废标吗)

  • 抖音短视频怎么合拍(抖音短视频怎么做才能赚钱)

    抖音短视频怎么合拍(抖音短视频怎么做才能赚钱)

  • 文件夹里面的文件怎么调换顺序(文件夹里面的文档怎么按我要求排序)

    文件夹里面的文件怎么调换顺序(文件夹里面的文档怎么按我要求排序)

  • 抖音转发的视频在哪里怎么找不到(抖音转发的视频无法查看怎么回事)

    抖音转发的视频在哪里怎么找不到(抖音转发的视频无法查看怎么回事)

  • 华为psmart是什么型号(华为psmart plus)

    华为psmart是什么型号(华为psmart plus)

  • 腾讯视频如何用手机号登录(腾讯视频如何用支付宝支付会员)

    腾讯视频如何用手机号登录(腾讯视频如何用支付宝支付会员)

  • HTML+CSS+JS网页设计期末课程大作业 京剧文化水墨风书画(html+css+js网页设计期末作业付源码)

    HTML+CSS+JS网页设计期末课程大作业 京剧文化水墨风书画(html+css+js网页设计期末作业付源码)

  • 企业视同销售的税法依据是什么?
  • 个人所得税是什么
  • 租入厂房再转租账要怎么做
  • 1%是什么税
  • 营业成本增加会导致
  • 白酒在哪个环节征收消费税
  • 工商不变新股东是什么
  • 税收用在哪些方面
  • 从公司账户转给个人账户取出来发给员工做过节费
  • 非税收入一般缴款书可以抵扣吗
  • 购置办公大楼
  • 当月支付租金没有发票
  • 外贸公司的出口清单
  • 人工服务费发票
  • 提高主营业务收入的意义
  • 小规模自开专票怎么交税
  • 对于不需要交强险的人
  • 现金购入库存商品的分录
  • 国有土地租金怎么交税
  • 销售购物卡如何开票
  • 企业从境外收回所得税
  • symtray.exe - symtray是什么进程 有何作用
  • 出售房产会计账务处理
  • 企业所得税申报流程
  • 存货损失进项税
  • 王者荣耀中孙尚香怎么玩
  • 独资子公司如何注册
  • 一般纳税人取得普票会计分录
  • 通用数据库软件
  • 工会举办的比赛有哪些
  • php sql语句
  • 发票开具的常见错误
  • La Selva生物站热带雨林树冠上的黑嘴巨嘴鸟,哥斯达黎加 (© Greg Basco/Minden Pictures)
  • 二手车销售发票的开具怎么处理?
  • thinkPHP5 tablib标签库自定义方法详解
  • php和ajax用哪个调用数据
  • 清算资金往来的余额方向
  • 农产品加计扣除1%怎么计算和会计分录
  • 使用命令查看
  • 材料采购计入哪个账本
  • 金融企业往来支出属于费用吗
  • 织梦如何采集文章
  • 企业所得税预缴少缴纳了,需要交滞纳金吗?
  • 外部审计查什么
  • 开发支出在资产负债表填哪一栏
  • 付款给个人
  • 汇兑损益应计入
  • 如何安装sql server2022
  • 预付发票能入账吗
  • 逾期交房违约金 已支付金额
  • 增值税发票税率计算公式
  • 专利技术评估价值7亿
  • 增值税附加税需要写进合同吗
  • 以自营方式建造固定资产
  • 差旅费包括哪些费用
  • 建筑业负数发票不填工程名称和地址可以吗
  • 预付账款要设明细科目吗
  • 一般纳税人会计分录
  • 发票边上的孔有什么用
  • 销售不动产计税税率
  • 培训费产生的差额怎么算
  • 外汇申报要在几天之内
  • 居间费用超过30%违法吗
  • 滴滴发票抬头是什么意思
  • 商用计算机英文
  • emule.td文件怎么打开
  • xp系统操作全程图解
  • cocos2dx drawcall优化
  • unity中mathf
  • qat开发
  • 用python的turtle画图代码
  • html图像元素
  • linux -lc
  • 前端js优化
  • js设置页面大小
  • 高博应诺官网
  • jquery 判断对象是否为空
  • 国家税务总局朝阳税务局
  • 山东地域分布
  • 三证合一怎么申报
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设