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:指令绑定的元素,可以用来直接操作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.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号
浙公网安备 33010602011771号