Vue.js入门 0xA 自定义指令

    如 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>

Vue.js入门 0xA 自定义指令

     钩子函数参数

•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——可从外部关闭的下拉菜单

Vue.js入门 0xA 自定义指令

     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__;
    }
})

Vue.js入门 0xA 自定义指令