第八章 自定义指令

编辑本文章

8.1  基本用法

  自定义指令的注册方法和注册组件很像,也分全局注册和局部注册。如注册一个v-focus指令:

<script type="text/javascript">
    //全局注册
    Vue.directive('focus',{
        //指令选项
    });
    //局部注册
    var app=new Vue({
        el:"#app",
        directives:{
            focus:{
                //指令选项
            }
        }
    })
</script>
View Code

  8.1.1 钩子函数组成

  自定义指令由几个可选钩子函数组成

    • bind:只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作。
    • inserted:被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。
    • update:被绑定元素所在的模板更新时调用,无论绑定值是否有变化。通过比较更新前后的绑定值来忽略不必要的模板更新。
    • componentUpdated:被绑定元素所在模板完成一次更新周期时调用。
    • unbind:只调用一次,指令与元素解绑时调用。

  如上面的v-focus指令,希望在元素插入父节点时获取到焦点:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>8.1 基本用法</title>
</head>
<body>
    <div id="app">
        <input type="text" v-focus>
    </div>
</body>
<script src="vue.js"></script>
<script type="text/javascript">
    //全局注册
    Vue.directive('focus',{
        //指令选项
        inserted:function (el) {
            el.focus();
        }
    });
    //局部注册
    var app=new Vue({
        el:"#app"
    });

</script>
</html>
View Code

  页面刷新即可获取到焦点

  

8.1.2 钩子函数参数

  钩子函数可选参数:

    • el:指令所绑定的元素,可以用来直接操作DOM
    • binding:一个对象,包含如下属性:
      • name:指令名称,不包括前缀v-
      • value:指令绑定值,如v-my-directive="1+1",则value值为2
      • oldValue:指令绑定的前一个值,仅在update和componentUpdate钩子中可用,无论值是否改变都可以用。
      • expression:绑定值的字符串形式。如v-my-directive="1+1",expression值是“1+1”,而不是2
      • arg:传给指令的参数。如v-my-directive:foo,arg的值为foo
      • modifiers:一个包含修饰符的对象。如v-my-directive.foo.bar,修饰符对象的值是{foo.true,bar.true}
    • vnode:Vue编译生成虚拟节点。
    • oldVnode:上一个虚拟节点,仅在update和componentUpdate钩子中可用。

 8.2  实战

8.2.1 开发一个可从外部关闭的下拉菜单

知识点:contains 方法是用来判断元素A 是否包含了元素B ,包含返回true,不包含返回false.

index.js

var app=new Vue({
    el:"#app",
    data:{
        show:false,
    },
    methods:{
        handleClose:function () {
            this.show=false;
        }
    }
});
View Code

clickoutside.js

//注册自定义指令

//指令名称clickoutside
Vue.directive('clickoutside',{
    //钩子函数bind
    bind:function(el,binding,vnode){
        /*
        * el:指令所绑定的元素,可以用来操作DOM
        * bingding:一个对象,拥有自己的属性
        * vnode:Vue编译生成的虚拟节点,这里用不上
        */
        function documentHandler(e) {
            //e:鼠标点击的DOM元素
            if(el.contains(e.target)){
                //点击的区域e.target是否在所绑定园区el的内部
                return false;
            }
            if(binding.expression){
                //判断我们的指令是否绑定了函数
                // <div class="main" v-clickoutside="handleClose">,实际我们指令绑定了handleClose函数

                //执行我们绑定的指令,这里e可传可不传,传入后可以在绑定的方法中操作DOM元素
                binding.value(e);
            }
        }

        //为el添加一个属性,绑定documentHandler方法,用于解绑时删除
        el.__vueClickOutside__=documentHandler;

        //为document添加click事件
        document.addEventListener('click',documentHandler);
    },

    //解绑时,为document删除绑定的事件,因为无法用this,所以这里用el来承载
    unbind:function (el, binding) {
        document.removeEventListener('click',el.__vueClickOutside__);
        delete el.__vueClickOutside__;
    }
})
View Code

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="style.css" type="text/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="vue.min.js"></script>
<script src="clickoutside.js" type="application/javascript"></script>

<!--包含new Vue()的组件需要放最后面-->
<script src="index.js" type="application/javascript"></script>
</body>
</html>
View Code

style.css

[v-cloak]{
    display: none;
}
.main{
    width: 125px;
}
button{
    display: block;
    width: 100%;
    color:#fff;
    background-color: #39f;
    border: 0px;
    padding: 6px;
    text-align: center;
    font-size: 12px;
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
    cursor: pointer;
    outline: none;
    position: relative;
}
button:active{
    top: 1px;
    left: 1px;
}
.dropdown{
    width: 100%;
    height: 150px;
    margin: 5px 0;
    font-size: 12px;
    background-color: #fff;
    -webkit-border-radius: 4px;
    -moz-border-radius: 4px;
    border-radius: 4px;
    box-shadow: 0 1px 6px rgba(0,0,0,.2);

}
.dropdown p{
    display: inline-block;
    padding: 6px;
}
View Code

扩展:使用esc键也可以关闭下拉框,应该如何做?

8.2.2 开发一个实时时间转换指令v-time

给定一个毫秒级别的时间戳,显示成指定的格式

index.js

var app=new Vue({
    el:"#app",
    methods:{

    },
    data:{
        timeNow:(new Date()).getTime(),
        timeBefore:1559373149411
    }
});
View Code

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>v-time</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>

<div id="app" v-cloak>
    <div v-time="timeNow"></div>
    <div v-time="timeBefore"></div>
</div>

<script src="vue.min.js"></script>
<script src="time.js"></script>
<script src="index.js"></script>
<script type="application/javascript">

</script>
</body>
</html>
View Code

time.js

var Time={
    getUnix:function () {
        var date=new Date();
        return date.getTime();
    },
    getTodayUnix:function () {
        //获取今天0时0分0秒的时间戳
        var date=new Date();
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);
        return date.getTime();
    },
    getYearUnix:function () {
        //获取今年1月1日 0时0分0秒的时间戳
        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) {
        //timestamp:服务器传的时间戳时间,比如文件创建的时间戳
        var now=this.getUnix();//获取当前时间戳
        var today=this.getTodayUnix();//获取今天0点时间戳
        var year=this.getYearUnix();//获取今年1月1日0点时间戳
        var timer=(now-timestamp) / 1000;//毫秒转换为秒

        //Math.floor()向下取整
        //Math.ceil()向上取整

        var tip=""
        if(timer<0){
            tip="刚刚"
        }else if(Math.floor(timer/60)<=0){
            //1分钟内
            tip="刚刚"
        }else if(timer<3600){
            //大于1分钟,小于1个小时
            tip=Math.floor(timer/60) + "分钟前"
        }else if(timer>=3600 && (timestamp-today >= 0)){
            //大于1小时,但是在今天内
            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);

        //定时器,一分钟更新一次;这里给保存下来,在unbind中可对其删除
        el.__timeout__=setInterval(function () {
            el.innerHTML=Time.getFormatTime(binding.value);
        },60000);
    },
    unbind:function (el) {
        clearInterval(el.__timeout__);
        delete el.__timeout__;
    }
})
View Code

 

posted @ 2019-05-15 22:56  丫丫625202  阅读(131)  评论(0编辑  收藏  举报