VUE探索第六篇-指令(directive)

一、什么是指令

     上一篇我们讲到组件,组件是为了实现代码复用,将业务逻辑,UI模板封装成一个自定义的"标签"供外部使用,但有些时候我们用普通的dom也能实现相关的功能,但需要对功能进行一些"改造",使我们的实现更简洁,此时就会用到我们的指令。

二、内置指令

vue提供大量的内置的指令,先来总览下这些指令:

v-bind:动态的绑定一个或者多个特性值。

v-on:绑定监听事件,用户监听事件的变化。

v-model:表单组件上的双向绑定,如input

v-show:条件渲染,确定元素的显示或者隐藏

v-html:富文本渲染,作用类似于innerHTML

v-for:循环指令,对修饰的元素或者模板进行循环

v-if,v-else,v-else-if,条件指令,这几条指令配合使用,对元素进行条件渲染。

v-pre,在模板中跳过编译过程,直接输出原始值,如<div v-pre>{{msg}}</div>,输出的是"{{msg}}"

v-cloak:这个指令保持在元素上直到关联实例结束编译;这个说法有点晦涩,<div >{{msg}}</div>,有时会先显示"{{msg}}",然后再显示实际绑定值,导致闪屏,利用v-cloak配合display:none使用,待元素准备完毕后再显示,可解决此问题。

v-once:仅渲染一次,后续的元素将不再渲染。

下面我们对常用的指令的一些使用方法进行说明

三、v-bind

v-bind作为绑定命令,可以使用在属性,样式,以及组件的数据传递。

1、属性

以img的src为例,我们想动态的改变img的地址,那么使用绑定命令,绑定data中的src

<!--属性绑定-->
<img v-bind:src="src">
...
data:function() {
    return {
        src:require('./assets/logo.png'),
      }
  }

2、样式

     有时我们要动态的改变class样式,vue无法直接对css操作,但是通过属性变量可以动态的切换不同的class。如在激活状态下我们要加载名为actionInfo的class,可以有以下几种表达方式。

class直接绑定data的属性,通过watch监听状态并改变属性值,从而改变样式

<!--绑定属性-->
<div v-bind:class="activeInfo">333</div>
...
data:function() {
    return {
        isActive:true,
        activeInfo:'activeInfo'
      }
  },
  watch:{
    isActive:function(val){
       val?this.activeInfo='activeInfo':this.activeInfo=''
    }
  }
...
.activeInfo{
  border:1px red solid;
}

我们可以再简单些,在模板中直接采用三元计算(数组模式)

<!--二元计算-->
<div v-bind:class="isActive?'activeInfo':''">111</div>

再简单些有没有(对象模式)

<div v-bind:class="{activeInfo:isActive}">2222</div>

还可以对于多条件的class进行操作

<!--数组模式-->
<div v-bind:class="[isActive?'activeInfo':'',info]">111</div>
<!--对象模式-->
<div v-bind:class="{activeInfo:isActive,info}">2222</div>
...
data:function() {
    return {
        isActive:true,
        activeInfo:'activeInfo',
        info:'info'
      }
  }
}
...
.activeInfo{
  border:1px red solid;
}
.info{
 color: green
}

    除了可以动态改变class样式,也可以动态改变内联的style样式,使用方式和上述一致,只是绑定的属性值是具体的样式,而不是class名而已

<!---对象模式-->
<div v-bind:style="{color:color,fontSize:fontSize+'px'}">444</div>
...
data:function() {
    return {
        color:'green',
        fontSize:40
      }
  }

这里要注意下,color,fontSize是js的css对象名,对应的css属性名是color,font-size,也可以用css属性名(需要用单引号)

<!---css的属性名-->
 <div v-bind:style="{'color':color,'font-size':fontSize+'px'}">444</div>

采用数组模式,需要注意的是,数组元素需要是对象。

<!--数组模式-->
<div v-bind:style="[color,fontSize]">5555</div>
....
data:function() {
    return {
        color:{
          color:'green'
        },
        fontSize:{
          fontSize:"40px"
        }
      }
  },

3、prop数组传递

父组件给子组件数组传递时,通过绑定父组件的对象,映射到子组件的prop上,这个在组件中有详细介绍,大家可以查阅。

v-bind输入闲麻烦,有缩写的方法

<div v-bind:style="[color,fontSize]">5555</div>

等价于:

<div :style="[color,fontSize]">5555</div>

      其实v-bind的作用就是声明式渲染,类似于{{activeInfo}},个人认为v-bind的设计有些画蛇添足,何不和声明式渲染合一呢,采用如下的方式就能搞定呢,当然这只是我的一些愚见。

<!--绑定属性-->
<div class="{{activeInfo}}">333</div>

四、v-on

    v-on指令监听DOM事件,主要分为两类,一类是对原生事件的监听,如button的click,form的submit等,一类是对自定义事件的监听,这类主要是子组件采用emit发送事件,父组件通过v-on绑定事件进行监听。

 首先我们来看下原生事件的监听的例子

<!--click-->
<button v-on:click="changeCls">555</button>
...
methods:{
    changeCls:function(e){
      console.log(e);
    }
  }

    v-on:click="changeCls"表示监听button的点击事件,处理方法为changeCls,在methods中定义了该方法体,默认传入event事件对象。

也支持内联处理器中的调用方法,可以带上自定义的参数

<!--内联方法-->
<button v-on:click="changeCls(name)">555</button>
export default {
  name: 'App',
  data:function() {
    return {
        name:'click me',
      }
  },
  methods:{
    changeCls:function(name){
      console.log(name);
    }
  }
}

有时方法中也需要使用原生的事件对象event,比如处理冒泡事件,可以通过$event传入:

<!--传入event,阻止冒泡-->
 <div v-on:click="changeCls('div',$event)">
     <button v-on:click="changeCls('button',$event)">555</button>
  </div>
  methods:{
    changeCls:function(name,event){
      //阻止冒泡
      event.stopPropagation();
      console.log(name);
    }
  }

为了方便处理冒泡,事件捕获等事件,vue提供了事件修饰符,上面的冒泡可以改写为:

<!--传入event,阻止冒泡-->
<div v-on:click="changeCls('div',$event)">
     <button v-on:click.stop="changeCls('button')">555</button>
</div>
methods:{
    changeCls:function(name){
      //阻止冒泡
      console.log(name);
    }
  }

通过加上.stop修饰符,使得代码更加简洁。相关的修饰符如下:

 .prevent,同e.preventDefault(),阻止默认行为。

  .capture,事件捕获模式。

  .self,只有event.target对象是节点本身时才会触发。

  .once,仅触发一次

 其他还有按键,鼠标等相关事件修饰符,可以查阅相关的用法。

另外对于自定义事件的监听,在组件中有详细的介绍。

五、v-if,v-else,v-else-if

     这几个指令组合一起使用,可以实现条件渲染。在实际项目中,我们经常会遇到根据不同的条件,而实现不同的页面呈现。比如,我们会根据请求返回的结果(成功,失败,其他未知)而展示不同的提示结果。

<!--条件渲染-->
    <div class="info">
      <!--成功-->
      <template v-if="result=='success'">
        <img src="./assets/success.png"/>
        <span>操作成功</span>
      </template> 
      <!--失败-->
      <template v-else-if="result=='error'">
        <img src="./assets/error.png"/>
        <span>操作失败</span>
      </template> 
      <!--其他未知-->
      <template v-else>
        <img src="./assets/unkown.png"/>
        <span>其他未知</span>
      </template> 
    </div>

当result=success时,看下实际的渲染dom结果:

仅将符合条件的代码模块渲染出来,其他的将不会加入到dom树,通过改变result的值,就可以渲染不同的代码块。

v-show指令也能控制代码块的显示和隐藏。它与v-if有如下的区别。

1、v-show总是将代码块渲染出来,然后控制其隐藏还是显示,所以代码块会在dom树中。

2、v-if仅将符合条件的渲染出来,加入dom树,其他的不会渲染。

所以两者区别与display,visibility原理一样。那实际项目中,应该用哪个呢,一般遵循以下原则:

如果频繁的切换,就用v-show,减少dom操作的开销。反之则用v-if,可以减少dom的节点数。

六、v-for

v-for指令实现列表渲染。列表是实际项目中最常用的组件,使用v-for绑定数组对象,很方便的实现列表渲染。

<!--tab标签-->   
<ul>
   <li v-for="tab in tabs">{{tab.name}}</li>
</ul>

 data:function() {
    return {
        tabs:[
               {name:'tab1'},
               {name:'tab2'},
               {name:'tab3'}
            ]
      }
  },

v-for语法与javascript的一致,很好理解。

有些情况下,我们要为每个元素增加唯一表示key,vue为我们提供可:key指令,配合v-for使用

 <!--tab标签-->   
 <ul>
    <li v-for="tab in tabs" :key="tab.id">{{tab.name}}</li>
 </ul>
data:function() {
    return {
        tabs:[
               {name:'tab1',id:'1'},
               {name:'tab2',id:'2'},
               {name:'tab3',id:'3'}
            ]
      }
  },

如果数据没有提供id作为我们的唯一标识,我们也可以使用索引值

<!--tab标签-->   
      <ul>
        <li v-for="(tab,index) in tabs" :key="index">{{tab.name}}</li>
      </ul>

v-for也可以对对象进行操作,我们把tabs改成对象模式

<!--tab标签-->   
      <ul>
        <li v-for="(value,key) in tabs" >{{key}}---{{value}}</li>
      </ul>
data:function() {
    return {
        tabs:{
          "1":"tab1",
          "2":"tab2",
          "3":"tab3"
        }

      }
  },

要特别注意:(value,key)第一个参数是value,第二是key,不能弄反了。

七、v-model

   vue支持数据的双向绑定,后台数据对页面元素的绑定,可以通过声明式渲染({{text}})以及v-bind指令实现,页面元素对后台数据的绑定就需要使用v-model了。

页面能改变数据的元素包括input,textarea,select,radio,checkbox等。

<!--v-model-->
<div>msg:{{message}}</div>
<input type="text"  v-model="message" >
...
data:function() {
    return {
        message:''
      }
  },

八、自定义指令

    当提供的这些内置指令不满足你的要求时,用户也可自定义指令,实现对dom的底层操作。比如我们可以自定义指令实现对输入的电话,身份证等进行校验。

  •    语法

     自定义指令可以分为全局指令和局部指令两种。

    全局指令,定义在Vue对象上:

Vue.directive('check',{
    //实现各种钩子

})

 局部指令

directives:{
    check:{
      //实现各类钩子
    }
  }

其中,第一个参数,"check"就是指令名,在使用时需要加上"v-"前缀,第二个参数指令的各种钩子,指令的具体实现需要在这些钩子中完成。指令的使用与原生的一致,在元素上增加"v-check"即可。

<div v-check ></div>    

接下来我们看各个钩子的实现:

Vue.directive('check',{
	/**
	 *绑定
	 *只调用一次,第一次绑定到元素时调用
	 **/
     bind:function(el, binding, vnode){
     	console.log("bind");
     },
    /**
	 *插入
	 *被绑定的元素插入到父节点时调用
	 **/
	inserted:function(el, binding, vnode){
       console.log("inserted");
	},
	/**
     *组件更新
     *根据获得的新值执行对应的更新,对于初始值也会调用一次
     **/
	update:function(el, binding, vnode,oldVnode){
       console.log("update");
	},
	/**
     *组件更新完成
     **/
	componentUpdated:function(el, binding, vnode,oldVnode){
       console.log("componentUpdated");
	},
	/**
     *解绑
     *只调用一次,指令与元素解绑时调用。
     **/
	unbind:function(el, binding, vnode){
       console.log("unbind");
	}
})

页面加载时执行:

bind

inserted

组件更新时执行

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.a.b,modifiers的值为{'a':true,'b':true}

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

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

  • 实例

    很多同学在实际项目中并不习惯自定义指令,因为很多时候,我们都可以找到其他的解决方案替代。那自定义指令有什么优势呢,我们看到它的钩子入参中都含有el,可以对DOM进行操作。VUE是数据驱动模式,但数据驱动并不是包治百病,有些情况下对DOM操作会更加方便。

下面我们实现一个表单数字型校验的实例,及时校验输入的字符,当非数字字符时给出提示,简便起见,将输入框颜色变红。

<!--数学校验指令-->
<div class="check">
   请输入手机号:<input v-check v-model="message"></input> 
</div>
Vue.directive('check',{	
	/**
     *组件更新
     *根据获得的新值执行对应的更新,对于初始值也会调用一次
     **/
	update:function(el, binding, vnode,oldVnode){
        var numberReg=/^[1-9]\d*$/;
        if (!numberReg.test(el.value)) {  
              //校验不通过
             el.style.border="4px red solid"; 
          } else {  
          	 //校验成功
             el.style.border="none";
          }
	}
})

大家可以考虑下,用一个统一指令实现对身份证,手机号,银行卡的校验。

发布了33 篇原创文章 · 获赞 95 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/tcy83/article/details/83934299