当前位置: 首页 > 科技观察

分享8个非常实用的Vue自定义命令

时间:2023-03-14 11:24:46 科技观察

【攻略】:入门级实例,结合Vue文档更好。推荐给新手学习,不直接用于生产环境。在Vue中,除了核心功能(v-model和v-show)的默认内置指令外,Vue还允许注册自定义指令。它的价值在于开发者在某些场景下需要对普通DOM元素进行操作。Vue自定义指令有全局注册和本地注册两种方式。我们先来看看全局指令的注册方式。通过Vue.directive(id,[definition])注册全局指令。然后在入口文件中调用Vue.use()。批量注册指令,新建directives/index.js文件importcopyfrom'./copy'importlongpressfrom'./longpress'//自定义指令constdirectives={copy,longpress,}exportdefault{install(Vue){Object.keys(directives).forEach((key)=>{Vue.directive(key,directives[key])})},}将代码复制到main.js中,调用importVuefrom'vue'importDirectivesfrom'./JS/directives'Vue.use(Directives)复制代码指令定义函数提供了几个钩子函数(可选):bind:只调用一次,当指令第一次绑定到元素上时,可以定义一个初始化动作,绑定时执行一次。inserted:绑定元素插入到父节点时调用(如果父节点存在,则可以调用,不一定存在于文档中)。update:绑定元素所在模板更新时调用,不管绑定值是否变化。通过比较更新前后的绑定值。componentUpdated:当绑定元素所在的模板完成一个更新周期时调用。unbind:仅在指令与元素解除绑定时调用一次。下面介绍几个有用的Vue自定义命令复制粘贴命令v-copy长按命令v-longpress输入框防抖命令v-debounce禁止表情和特殊字符v-emoji图片懒加载v-LazyLoad权限验证命令v-premission实现页面水印v-waterMarker拖动命令v-draggablev-copy需求:实现鼠标右键一键复制文本内容粘贴。思路:1.动态创建textarea标签,设置readOnly属性,移出可见区域2.将要复制的值赋值给textarea标签的value属性,插入到body中3.选中valuetextarea,复制4.在body中插入textareaRemove5.第一次调用时绑定事件,解绑时移除事件constcopy={bind(el,{value}){el.$valuevalue=valueel.handler=()=>{if(!el.$value){//当值为空时,给出提示。可以根据项目UI精心设计console.log('nocopycontent')return}//动态创建textarea标签consttextarea=document.createElement('textarea')//设置textarea为readonly防止自动激活iOS下键盘的,同时将textarea移出可见区域textarea.readOnly='readonly'textarea.style.position='absolute'textarea.style.left='-9999px'//赋值要复制到textarea标签的value属性textarea.value=el.$value//将textarea插入bodydocument.body.appendChild(textarea)//选择value并复制textarea.select()constresult=document.execCommand('Copy')if(result){console.log('复制成功')//可以根据项目UI精心设计}document.body.removeChild(textarea)}//绑定点击事件,也就是so-调用一键复制el.addEventListener('click',el.handler)},//当传入的值更新时,componentUpdated(el,{value}){el.$valuevalue=value}为triggered,//当指令与元素解除绑定时,移除事件绑定unbind(el){el.removeEventListener('click',el.handler)},}exportdefaultcopy复制代码使用:添加v-copy和复制文本到Dom到复制代码v-longpress要求:实现长按,用户需要按住按下按钮几秒,触发相应事件思路:1.创建一个定时器,2秒后执行函数2.当用户按下按钮启动定时器时,触发mousedown事件;当用户释放按钮时调用mouseout事件3.如果mouseup事件在2秒内触发,则计时器将被清除为正常的点击事件。4.如果定时器在2秒内没有清零,则判断为长按,可以执行相关功能。5.在移动端,应该考虑touchstart和touchend事件constlongpress={bind:function(el,binding,vNode){if(typeofbinding.value!=='function'){throw'callbackmustbeafunction'}//定义变量letpressTimer=null//创建定时器(2秒后执行函数)letstart=(e)=>{if(e.type==='click'&&e.button!==0){return}if(pressTimer===null){pressTimer=setTimeout(()=>{handler()},2000)}}//取消定时器letcancel=(e)=>{if(pressTimer!==null){clearTimeout(pressTimer)pressTimer=null}}//运行函数consthandler=(e)=>{binding.value(e)}//添加事件监听el.addEventListener('mousedown',start)el.addEventListener('touchstart',start)//取消timerel.addEventListener('click',cancel)el.addEventListener('mouseout',cancel)el.addEventListener('touchend',cancel)el.addEventListener('touchcancel',cancel)},//当传入的值为updatedtriggercomponentUpdated(el,{value}){el.$valuevalue=value},//指令与元素解除绑定时,解除事件绑定解除绑定(el){el.removeEventListener('点击',el。handler)},}exportdefaultlongpress复制代码使用:在Dom中添加v-longpress和回调函数复制代码v-debouncebackground:in开发中,有些提交和保存按钮有时会在短时间内多次点击,会多次重复请求后端接口,造成数据混乱。比如新建表单的提交按钮,多次点击会增加重复数据需求:防止按钮在短时间内被多次点击,使用防抖功能限制只能点击一次在规定的时间内。思路:1.定义一个延迟执行的方法。如果在延迟时间内再次调用该方法,将重新计算执行时间。2.给点击方法绑定事件。constdebounce={inserted:function(el,binding){lettimerel.addEventListener('click',()=>{if(timer){clearTimeout(timer)}timer=setTimeout(()=>{binding.value()},1000)})},}exportdefaultdebounce复制代码使用:Dom添加v-debounce和回调函数复制代码v-emoji背景:开发中遇到的表单输入往往对输入内容有限制,例如表情和特殊字符不能输入,只能输入数字或字母。我们通常的做法是处理每个表单的on-change事件。像这样复制代码比较大,维护起来比较困难,所以我们需要自定义一个命令来解决这个问题。需求:根据正则表达式,设计处理表单输入规则的自定义指令。下面是禁止输入表情符号和特殊字符的例子。letfindEle=(parent,type)=>{returnparent.tagName.toLowerCase()===type?parent:parent.querySelector(type)}consttrigger=(el,type)=>{conste=document.createEvent('HTMLEvents')e.initEvent(type,true,true)el.dispatchEvent(e)}constemoji={bind:function(el,binding,vnode){//可以根据需求自定义正则规则varregRule=/[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,.?!...—&$=()-+/*{}[\]]|\s/glet$inp=findEle(el,'input')el.$inp=$inp$inp.handle=function(){letval=$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)},}exportdefaultemoji复制代码使用:在需要验证的输入框添加v-emoji复制代码v-LazyLoad背景:在电商项目中,往往会有很多图片,比如banner广告图,菜单导航图,美团以及其他商家列表头等。图片过多、图片过大往往会影响页面的加载速度,导致用户体验不好,因此优化图片的懒加载势在必行。需求:实现一个懒惰的图片加载命令,只加载浏览器可见区域的图片。思路:图片懒加载的原理主要是判断当前图片是否到达可见区域。核心逻辑是获取所有图片Dom,遍历每张图片,判断当前图片是否在可见区域内,如果在,则设置图片。src属性,否则将显示默认图像。图片的延迟加载可以通过两种方式实现。一种是绑定srcoll事件进行监听,另一种是使用IntersectionObserver判断图片是否到达可见区域,但是存在浏览器兼容性问题。下面封装了一条兼容这两种方法的懒加载指令来判断浏览器是否支持IntersectionObserverAPI。如果支持,则使用IntersectionObserver实现懒加载,否则使用srcoll事件监听+节流的方式。constLazyLoad={//安装方法install(Vue,options){constdefaultSrc=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){vario=newIntersectionObserver((entries)=>{constrealSrc=el.dataset.srcif(entries[0].isIntersecting){if(realSrc){el.src=realSrcel.removeAttribute('data-src')}}})io.observe(el)},//监听滚动事件listenerScroll(el){consthandler=LazyLoad.throttle(LazyLoad.load,300)LazyLoad.load(el)window.addEventListener('scroll',()=>{handler(el)})},//加载图片load(el){constwindowHeight=document.documentElement.clientHeightconstelelTop=el.getBoundingClientRect().topconstelelBtm=el.getBoundingClientRect().bottomconstrealSrc=el.dataset.srcif(elTop-windowHeight<0&&elBtm>0){if(realSrc){el.src=realSrcel.removeAttribute('data-src')}}},//节流throttle(fn,delay){lettimerletprevTimereturnfunction(...args){constcurrTime=Date.now()constcontext=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)}},}exportdefaultLazyLoad复制代码使用,将组件中标签的src替换为v-LazyLoad复制代码v-权限后台:在一些后台管理系统中,我们可能需要根据用户角色判断一些操作权限。很多时候我们粗略的给一个元素加上v-if/v-show来进行显示和隐藏,但是如果判断条件比较繁琐,有多个地方需要判断。这样的代码不仅不优雅而且冗余。对于这种情况,我们可以使用全局自定义指令来处理。需求:自定义一个权限命令,显示和隐藏需要权限判断的Dom。思路:1.自定义一个权限数组2.判断用户的权限是否在这个数组中,如果是则显示,否则去掉DomfunctioncheckArray(key){letarr=['1','2','3','4']letindex=arr.indexOf(key)if(index>-1){returntrue//有权限}else{returnfalse//无权限}}constpermission={inserted:function(el,binding){letpermission=binding.value//获取v-permission的值if(permission){lethasPermission=checkArray(permission)if(!hasPermission){//没有权限移除Dom元素el.parentNode&&el.parentNode.removeChild(el)}}},}exportdefaultpermission复制代码使用:给v-permission赋值判断permissionbutton1Permissionbutton2

复制代码vue-waterMarker需求:给整个页面添加背景水印思路:1.使用canvas特性生成base64格式化图片文件,设置其字体大小、颜色等。2.设置为背景图实现页面或组件水印效果函数addWaterMarker(str,parentNode,font,textColor){//水印文字,父元素,字体,文字颜色varcan=document.createElement('canvas')parentNode.appendChild(can)can.width=200can.height=150can.style.display='none'varcancans=can.getContext('2d')cans.rotate((-20*Math.PI)/180)罐头。font=font||'16pxMicrosoftJhengHei'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')+')'}constwaterMarker={bind:function(el,binding){addWaterMarker(binding.value.text,el,binding.value.font,binding.value.textColor)},}exportdefaultwaterMarker复制代码使用,设置水印文字、颜色、字号复制代码v-draggable需求:实现一个拖拽command,可以在页面可见区域拖放元素。思路:1.设置被拖动的元素为相对定位,其父元素为绝对定位。2.记录鼠标按下时(onmousedown)目标元素的当前left和top值。3.当鼠标移动时(onmousemove),计算每次移动的水平距离和垂直距离的变化值,改变元素的left和top值4.当鼠标松开时完成一次拖动(onmouseup)constdraggable={inserted:function(el){el.style.cursor='move'el.onmousedown=function(e){letdisx=e.pageX-el.offsetLeftletdisy=e.pageY-el.offsetTopdocument.onmousemove=函数(e){让x=e。pageX-disxlety=e.pageY-disyletmaxX=document.body.clientWidth-parseInt(window.getComputedStyle(el).width)letmaxY=document.body.clientHeight-parseInt(window.getComputedStyle(el).height)if(x<0){x=0}elseif(x>maxX){x=maxX}if(y<0){y=0}elseif(y>maxY){y=maxY}el.style.left=x+'px'el.style.top=y+'px'}document.onmouseup=function(){documentdocument.onmousemove=document.onmouseup=null}}},}exportdefaultdraggable复制代码使用:addv-draggabletotheDomto复制所有指令源码https://github.com/Michael-lzg/v-directives