如 v-if、 v-show 等,这些丰富的内置指令能满足我们的绝大部分业务需求,不过在需要一些特殊功能时,我们仍然希望对 DOM 进行底层的操作,这时就要用到自定义指令。
基本用法
自定义指令的注册方法和组件很像,也分全局注册和局部注册
//全局注册
Vue.directive('focus',{
//指令选项
});
//局部注册
var app = new Vue({
el:'#app',
directives:{
focus:{
//指令选项
}
}
})
自定义指令的选项是由几个钩子函数组成的,每个都是可选的。
•bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作.
•inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中).
•update:被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。
•componentUpdated:被绑定元素所在模板完成一次更新周期时调用.
•unbind:只调用一次,指令与元素解绑时调用。
<div id="app">
<input type="text" v-focus>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
Vue.directive('focus',{
//元素插入后获得焦点,变为可输入状态
inserted:function(el){
//聚焦元素
el.focus();
}
})
var app = new Vue({
el:'#app'
})
</script>
钩子函数参数
•el指令所绑定的元素,可以用来直接操作DOM.
•binding一个对象,包含以下属性:
>name指令名,不包括v-前缀。
>value指令的绑定值,例如v-my-directive="1+l",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钩子中可用。
<script>
Vue.directive('test',{
bind:function(el,binding,vnode){
var keys = [];
for(var i in vnode){
keys.push(i);
}
el.innerHTML =
'name:'+binding.name+'<br>'+
'value:'+binding.value+'<br>'+
'expression:'+binding.expression+'<br>'+
'argument:'+binding.arg+'<br>'+
'modifiers:'+JSON.stringify(binding.modifiers)+'<br>'+
'vnode keys:'+keys.join(',')
}
})
var app = new Vue({
el:'#app',
data:{
message:'some text'
}
})
</script>
执行结果:
name:test
value:some text
expression:message
argument:msg
modifiers:{"a":true,"b":true}
vnode keys:tag,data,children,text,elm,ns,context,fnContext,fnOptions,fnScopeId,key,componentOptions,componentInstance,parent,raw,isStatic,isRootInsert,isComment,isCloned,isOnce,asyncFactory,asyncMeta,isAsyncPlaceholder
Demo——可从外部关闭的下拉菜单
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>可从外部关闭的下拉菜单</title>
<link rel="stylesheet" type="text.css" href="style.css">
</head>
<body>
<div id="app" v-cloak>
<div class="main" v-clickoutside="handleClose">
<button @click="show=!show">点击显示下拉菜单</button>
<div class="dropdown" v-show="show">
<p>下拉框的内容,点击外面区域可以关闭</p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="clickoutside.js"></script>
<script src="index.js"></script>
</body>
</html>
index.js
var app = new Vue({
el:'#app',
data:{
show:false
},
methods:{
handleClose:function(){
this.show = false;
}
}
});
clickoutside.js
Vue.directive('clickoutside',{
bind:function(el,binding,vnode){
function documentHandler(e){
if(el.contains(e.target)){
return false;
}
if(binding.expression){
binding.value(e);
}
}
el.__vueClickOutside__ = documentHandler;
document.addEventListener('click',documentHandler);
},
unbind:function(el,binding){
document.removeEventListener('click',el.__vueClickOutside__);
delete el.__vueClickOutside__;
}
});
style.css
[v-cloak]{
display: none;
}
.main{
widows: 125px;
}
button{
display: block;
width: 100%;
color: #fff;
background-color: #39f;
border:0;
padding: 6px;
text-align: center;
font-size: 12px;
border-radius: 4px;
cursor: pointer;
outline: none;
position: relative;
}
button:active{
top:1px;
left: 1px;
}
.dropdown{
width: 100%;
height: 5px 0;
margin: 5px 0;
font-size: 12px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 1px 6px rgba(0, 0, 0, .2)
}
.dropdown p{
display: inline-block;
padding: 6px;
}
Demo——实时时间转换指令 v-time
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>时间转换指令</title>
</head>
<body>
<div id='app' v-cloak>
<div v-time="timeNow"></div>
<div v-time="timeBefore"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="time.js"></script>
<script src="index.js"></script>
</body>
</html>
index.js
var app = new Vue({
el:'#app',
data:{
timeNow:(new Date()).getTime(),
timeBefore:1488930695721
}
});
time.js
var Time = {
//获取当前时间戳
getUnix:function(){
var date = new Date();
return date.getTime();
},
//获取今天0点0分0秒的时间戳
getTodayUnix:function(){
var date = new Date();
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
return date.getTime();
},
//获取今年1月1日0点0分0秒的时间戳
getYearUnix:function(){
var date = new Date();
date.setMonth(0);
date.setDate(1);
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
date.setMilliseconds(0);
return date.getTime();
},
//获取标准年月日
getLastDate:function(time){
var date = new Date(time);
var month = date.getMonth()+1<10?'0'+(date.getMonth()+1):date.getMonth()+1;
var day = date.getDate()<10?'0'+date.getDate():date.getDate();
return date.getFullYear()+'-'+month+"-"+day;
},
//转换时间
getFormatTime:function(timestamp){
var now = this.getUnix();//当前时间戳
var today = this.getTodayUnix();//今天0点时间戳
var year = this.getYearUnix();//今年0点时间戳
var timer = (now - timestamp)/1000;//转换为秒级时间戳
var tip ='';
if(timer<=0){
tip = '刚刚';
}else if(Math.floor(timer/60)<=0){
tip = '刚刚';
}else if(timer<3600){
tip = Math.floor(timer/60)+'分钟前';
}else if(timer>=3600&&(timestamp-today>=0)){
tip = Math.floor(timer/3600)+'小时前';
}else if(timer/86400<=31){
tip = Math.ceil(timer/86400)+'天前';
}else{
tip = this.getLastDate(timestamp);
}
return tip;
}
}
Vue.directive('time',{
bind:function(el,binding){
el.innerHTML = Time.getFormatTime(binding.value);
el.__timeout__ = setInterval(function(){
el.innerHTML = Time.getFormatTime(binding.value);
},60000);
},
unbind:function(el){
clearInterval(el.__timeout__);
delete el.__timeout__;
}
})