Vue自定义指令及使用
1.什么是指令在学习Vue的时候,肯定会接触到指令,那么什么是指令呢?Vue为页面和数据提供了一些更方便的输出。这些操作称为指令,用v-xxx表示,如html页面中的属性
,如angular中的ng,以-xxx开头的称为指令。指令中封装了一些DOM行为,属性的组合作为代码。该代码具有相应的值。根据不同的值,会绑定相关的DOM操作,即可以在vue中进行一些模板操作一些常用的内置v-command:v-text:元素的innerText属性,只能使用在double标签中,效果同{{}},较少使用v-html:元素的innerHTML,其实是为元素赋值的innerHTMLv-show:元素显示和隐藏,切换为主在CSS样式上。如果确定隐藏,则在元素的样式中加入display:nonev-if:元素的插入和移除操作等同于元素的销毁和创建。如果表达式的值为false,将留下一个作为标记。如果以后v-if的值为true,则在此处插入该元素(如果if中有else,就别管了)。v-else-if:前一个相邻元素必须有v-if或v-else-ifv-else:前一个相邻元素必须有v-if或v-else-if,ifv-if和v-else-if有对应的表达式,那么v-else可以直接写成v-for:用于循环渲染一组数据(数组或对象)。必须使用特定语法:v-for="表达式中的别名"。注意:当v-for和v-if在同一个节点时,v-for的优先级高于v-if。即v-if会在每个v-for循环中运行v-on:主要用来监听dom时间,然后进行一些操作。缩写为【@】v-model:用于在input/textarea等表单控件上创建双向数据绑定。v-bind:动态绑定一个或多个属性,常用于绑定class、style、href等。v-once:组件和元素只渲染一次,数据变化时不会重新渲染。v-if和v-show的比较:v-if是真正的条件渲染,因为它会确保条件块内部的事件监听器和子组件在切换过程中被适当地销毁和重建。v-if也是惰性的,如果在初始渲染时条件为假,它什么都不做,直到第一次条件为真,然后条件块将开始渲染。相比之下,v-show就简单多了,无论初始条件如何,元素都会一直被渲染,只是简单的根据css进行切换。一般而言,v-if的切换开销较高,而v-show的初始渲染开销较高。所以,如果需要非常频繁的切换,最好使用v-show,如果条件在运行时很少改变,最好使用v-if。在实际的开发过程中,这些内置的指令可能并不能满足所有的需求,或者你想给元素附加一些特殊的功能。这时候我们就需要用到Vue提供的一个强大灵活的功能“自定义命令”。官方api文档中有一句话:要对普通DOM元素进行低级操作,这时候就会用到自定义指令。也就是说,自定义指令解决的问题或者使用场景是对普通DOM元素进行低级操作,所以我们不能盲目地乱用自定义指令。2.自定义指令的钩子函数Vue提供了几种自定义指令的钩子函数:bind:指令第一次绑定到元素时调用,只执行一次。可以在这里进行一次性的初始化设置。inserted:绑定元素插入父节点DOM时调用(只保证父节点存在)。update:组件更新时调用。componentUpdated:组件和子组件更新时调用。unbind:当指令与元素解除绑定时调用,只执行一次。注意:除了update和componentUpdated钩子函数外,每个钩子函数都包含三个参数:el、binding、vnode。在每个函数中,第一个参数总是el,表示command绑定的dom元素,el参数是一个原生的JS对象,所以可以使用Vue自定义指令直接与DOM打交道。Binding是一个包含以下属性的对象:name,value,oldValue,expression,arg,modifiersoldVnode只在update和componentUpdated中,除了el之外,binding和vnode属性都是只读的钩子函数,在hook中生效。说白了就是生命周期,就是当一条指令绑定到一个元素上时,指令内部有5个生命周期事件函数。接下来创建一个case,看看这些钩子函数的触发:
Thisisapieceoftextexportdefault{......directives:{test:{bind(){console.log('bind')},inserted(){console.log('inserted')},update(){console.log('update')},componentUpdated(){console.log('componentUpdated')},unbind(){console.log('unbind')}}}}结果:页面渲染时,会触发bind和inserted函数。那么其他三个钩子函数什么时候触发呢?官方关于update的解释:update:组件的VNode更新时调用,但也有可能发生在它的子VNode更新之前。指令的值可能已更改,也可能未更改。但是可以通过比较更新前后的值来忽略不必要的模板更新(钩子函数参数详见下文)。我有点困惑,“组件的VNode”是指当前绑定到指令的dom元素吗?如果是这样,是否会在当前元素的状态发生变化时触发更新?使用v-show切换元素的显示和隐藏如下:Thisisanotherpieceoftextexportdefault{data(){return{show:true}}}默认情况下,仍然会触发bind和inserted。当点击按钮切换元素的显示时,结果如下:即当元素的样式改变时,会触发update和componentUpdated函数。使用v-if会触发哪个事件?Thisisanotherpieceoftext结果:发现触发unbind是因为v-if是删除或重建dom元素。当指令绑定的元素被销毁时,会触发指令的unbind事件,新创建的显示仍然会触发bind和inserted。总结:bind():当指令绑定到一个HTML元素时触发inserted():当指令绑定的元素插入父节点时触发绑定到元素的vue数据)有变化时触发componentUpdated():执行update()时触发unbind():指令绑定的元素从dom中删除时触发,输入框自动获得焦点(官方例子)。(2.点击下拉菜单以外的区域,隐藏菜单。(3.验证输入的邮箱地址和电话号码。以上几种情况,在做项目的时候可以用其他方法代替,但是vue自定义指令可以在一个地方定义,全局调用,代码简洁高效,更容易维护。指令的注册方式与“filter”、“mix-in”和“component”的注册方式一样分为两种:一种是全局注册,一种是局部注册。3.全局指令//给元素添加一个随机背景 Vue.directive('bgcolor',{bind:function(el,binding,vnode){el.style.backgroundColor="#"+Math.random().toString(16).slice(2,8);}})注意:定义时,指令名前不需要加v-前缀。调用时,必须在指定名称中加上v-前缀才能调用4.部分说明//与数据同级,methodsmethods:{},directives:{bgcolor:{bind:function(el,binding){el.style.backgroundColor="#"+Math.random().toString(16).slice(2,8);}}}个人比较喜欢用全局注册的方式,因为既然用了自定义指令,应该是通用的,可以复用的。因此,提供整个项目使用的指令更有价值,而不仅仅是在某个组件内。如果是单个地方使用,直接拉出功能就可以了,何必呢。5.带参数的自定义指令binding.value.color;}})6.函数缩写如果你想在bind和update的时候触发相同的行为而不关心其他的hooks,你可以这样写:://globalVue.directive('bgcolor',function(el,binding){el.style.backgroundColor=binding.value})//Localdirectives:{bgcolor:(el,binding)=>{el.style.backgroundColor=binding.value}}7.应用实例熟悉instructions创建方法和参数后,我们用它来创建两个实际案例。1.使用指令实现点击空白子菜单隐藏的功能,具体代码如下://clickOutside.jsexportdefault{bind(el,binding){constself={}//定义一个私有变量,其中可以在unbind事件监听器中解除绑定self.documentHandler=(e)=>{if(el.contains(e.target)){//这里判断绑定的元素是否包含点击元素,如果包含则返回false}if(binding.value){//判断指令中是否绑定了一个值binding.value(e)//如果绑定了一个函数,则调用该函数,其中binding.value为handleClose方法}returntrue}document.addEventListener('click',self.documentHandler)},unbind(el){//解除绑定事件监听document.removeEventListener('click',self.documentHandler)deleteself.documentHandler}}在组件中使用:
//全局注册Vue.directive('imgUrl',function(el,binding){el.style.backgroundColor='#FEFEFE'//设置背景颜色varimg=newImage()img.src=binding.value//binding.value指令后的参数img.onload=function(){el.style.backgroundColor=''el.src=binding.value}})