8.自定义指令
8.1 基本用法
//全局注册
Vue.directive('forcus',{
//指令选项
});
//局部注册
var app = new Vue({
el:'#app',
directives:{
focus:{
//指令选项
}
}
})
自定义指令的选项是由几个钩子函数组成的。
bind:只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作.inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。update:被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。componentUpdated:被绑定元素所在模板完成一次更新周期时调用。unbind:只调用一次,指令与元素解绑时调用 。
//directive: focus v-focus (自动加V)
div#app
input[type="text"][v-focus]
script
Vue.directive('focus',{
inservted:function(el){
el.focus();
}
});
var app = new Vue({
el:'#app'
})
每个钩子函数都有几个参数可用:
el:指令绑定的元素,可以用来直接操作DOMbinding:一个对象,包含以下属性:name指令名,不包括v-前缀。value指令的绑定值,例如 v-my-directive="1 + 1 ",value 的值是2oldValue指令绑定的前一个值,仅在 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}。
vnodeVue编译生成的虚拟节点oldVnode上一个虚拟节点仅在 update 和 componentUpdated 钩子中可用。
//绑定时输出一些属性
div#app
div[v-test:msg.a.b="message"]
script
Vue.directive('test',{
bind:function(el,binding,vnode){
var keys=[];
for(let i in vnode){
keys.push(i);
}
el.innerHTML=
'name: '+binding.name+'<br>'+
'value: '+binding.value+'<br>'+
'expression: '+binding.expression+'<br>'+
'argument: '+binding.arg+'<br>'+
'mpdifiers: '+JSON.stringify(binding.modifiers)+'<br>'+
'vnode keys: '+keys.json(', ');
}
});
var app = new Vue({
el:'#app',
data:{
message:'some text'
}
})
一般在bind钩子里绑定一些时间,比如在 document 上用 addEventListener绑定,在unbind里用removeEventistener解绑。
div#app
div[v-test="{msg:'hello',name:'Aresn'}"]
script
Vue.directive('test',{
bind:function(el,binding,vnode){
console.log(binding.value.msg);
console.log(binding.value.name);
}
});
var app = new Vue({
el:'#app'
})
8.2 实战
下拉菜单
实现点击空白处关闭菜单
index.html , clickoutside.js , style.css
//#index.html[v-cloak]
div#app
div.main[v-clickoutside.esc="handleClose"]
button[@click="show=!show"]{点击显示下拉菜单}
div.dropdown[v-show="show"]
p{下拉框的内容,点击外面区域可关闭}
script src="clcikoutside.js"
script
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) || e.keyup==27){
if(binding.expression){
binding.value();
}
}else{
return false;
}
}
el.__vueClickOutside__=documentHandler;
document.addEventListener('click',documentHandler);
if(binding.modifiers.esc) document.addEventListener('keyup',documentHandler);
};
unbind:function(el,binding){
document.removeEventListener('click',el__vueClickOutside__);
if(binding.modifiers.esc) document.removeEventListener('keyup',el.__vueClickOutside__);
delete el.__vueClickOutside__;
}
})
//-------------------------------
//style.css
[cloak]{
display:none;
}
.main{
width:100px
}
button{
dispaly:block;
position:relative;
width:100%;
color:#fff;
background-color:#39f;
border:0;
text-align:center;
font-size:12px,
border-radius:4px;
outline:none;
cursor:pointer;
}
button:active{
top:1px;
left:1px;
color:red;
}
.dropdown{
width:100%;
height:110px;
margin:5px 0;
font-size:12px;
background-color:#fff;
border-radius:15px;
box-shadow:0 5px 16px rgba(0,0,0.2);
}
.dropdown p{
display:inline-block;
padding:6px
}
实时时间转换
时间转换的逻辑:
- 1分钟以前,显示“刚刚”
- 1分钟~1小时之间,显示“xx分钟前”
- 1小时~1天之间,显示“xx小时前”(<24小时,但是已经过一天的显示“1天前”)
- 1天~1个月(31天)之间,显示“xx天前”
- 大于一个月,显示“xx年xx月xx日”
//index.html
div#app
v-time="tmeNow"
v-time="tmeBefore"
script
app=new Vue
data:{
timeNow=(new Date()).getTime(),
timeBefore:function(){
var date=new Date();
date.setDate(5);
return date.getTime();
}()
}
//------------------------------
//time.js
var Time={
//获取当前时间戳
getUnix:function(){
var date=new Date();
return date.getTime();
},
//获取今天0点0分0秒的时间戳
getTodayUnix:function(){
var date = new Date();
date.setSeconds(0);
date.setMinutes(0);
date.setHours(0);
date.setMilliseconds(0);
//console.log('今天的时间:'+date);
return date.getTime();
},
//获取今年1月1日0点0分0秒的时间戳
getYearUnix:function(){
var date=new Date();
date.setMonth(0);
date.setDate(1);
date.setSeconds(0);
date.setMinutes(0);
date.setHours(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.getDay()<10 ? '0'+date.getDay() : date.getDay();
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='刚刚';
if(Math.floor(timer/60)<=0 ){ //放一块会有问题吗? 是为了提高效率?// timer<=60不一样吗
tip='刚刚';
}else if(timer<3600){
tip=Math.floor(timer/60)+'分钟前';
//console.log('?分钟'+tip)
}else if(timer>=3600 && (timestamp-today>=0)){ //timer>=3600 应该可以去掉吧
//timestamp > today? timestamp是当前的时间
//console.log('?小时');
tip=Math.floor(timer/3600)+'小时前';
}else if(timer/86400 <=31){ //24*3600
//console.log('天');
tip=Math.ceil(timer/86400)+'天前';
}else{
//console.log('getlastDate');
tip=this.getLastDate(timestamp);
}
var date1=new Date(now);
var date2 = new Date(today);
var date3 = new Date(timestamp);
//console.log('now:'+date1);
//console.log('today:'+date2);
//console.log('timestamp:'+date3);
//console.log('now-timestamp:'+(now-timestamp));
//console.log('timestamp-today:'+(timestamp-today));
return tip;
}
};
Vue.directive('time',{
bind:function(el,binding){
//第一次会延迟5秒
el.innerHTML=Time.getFormatTime(binding.value),
el.__timeout__=setInterval(function(){
el.innerHTML=Time.getFormatTime(binding.value);
},5000);
},
unbind:function(el){
clearInterval(el.__timeout__);
delete el.__timeout__;
}
})

浙公网安备 33010602011771号