08.vue-charp-08 自定义指令

Posted easy5weikai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了08.vue-charp-08 自定义指令相关的知识,希望对你有一定的参考价值。

基本用法

自定义指令的选项是由几个钩子函数组成的,每个都是可选的。

  • bind:

    只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。

  • inserted:

    被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。

  • update:

    被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。

  • componentUpdated:

    被绑定元素所在模板完成一次更新周期时调用。

  • unbind:

    只调用一次,指令与元素解绑时调用。

基本使用

<body>
    <div id="app">
        <input type="text" v-focus>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        //全局注册指令
        Vue.directive(‘focus‘, {
            inserted: function (el, binding, vnode, oldVnode) {
                console.log("focus->el:", el);
                console.log("focus->binding:", binding);
                console.log("focus->vnode:", vnode);
                console.log("focus->oldVnode:", oldVnode);

                // 聚焦元素
                el.focus();
            }
        })
        var app = new Vue({
            el: ‘#app‘
        })
    </script>
</body>

钩子函数参数

每个钩子函数都有几个参数可用,比如上面我们用到了el。它们的含义如下: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 }。
  • vnode Vue编译生成的虚拟节点,在进阶篇中介绍。
  • oldVnode 上一个虚拟节点仅在update和componentUpdated 钩子中可用。
<body>
    <div id="app">
        <div v-test:msg.a.b="message"></div>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <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>
</body>

自定义指令-传入对象

<body>
    <div id="app">
        <div v-test="{msg: ‘hello‘, name: ‘Aresn‘}"></div>
    </div>
    <script src="../lib/vue.2.6.11.js"></script>
    <script>
        Vue.directive(‘test‘, {
            bind: function (el, binding, vnode) {
                console.log(binding.value.msg);
                console.log(binding.value.name);

                el.innerHTML =
                    ‘binding.value.msg: ‘ + binding.value.msg + ‘<br>‘ +
                    ‘binding.value.name: ‘ + binding.value.name + ‘<br>‘;

            }
        });
        var app = new Vue({
            el: ‘#app‘

        })
    </script>
</body>

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

点击用户头像和名称,会弹出一个下拉菜单,然后点击页面中其他空白区域(除了菜单本身外),菜单就关闭了。

index.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Vue 示例:从外部关闭的下拉菜单</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="../../lib/vue.2.6.11.js"></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); //binding.value()就是用来执行当前上下文 methods中指定的函数的
            }
        }

        //与Vue 1.x不同的是,在自定义指令中,不能再用this.xxx的形式在上下文中声明一个变量,
        //所以用了el.__vueClickOutside__引用了documentHandler,
        //这样就可以在unbind钩子里移除对document的click事件监听。如果不移除,当组件或元素销毁时,它仍然存在于内存中
        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{
    width: 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: 150px;
    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;
}

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

本示例就来实现这样一个自定义指令v-time,将表达式传入的时间戳实时转换为相对时间,

示例所用的时间戳都是毫秒级的,时间转换的逻辑:

  • 1分钟以前,显示“刚刚”。
  • 1分钟~1小时之间,显示“xx分钟前”。
  • 1小时~1天之间,显示“xx小时前”。
  • 1天~1个月(31天)之间,显示“xx天前”。
  • 大于1个月,显示“xx年xx月xx日”。

index.html

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Vue 示例:实时时间转换器</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>

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

    <script src="../../lib/vue.2.6.11.js"></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__;
    }
});

以上是关于08.vue-charp-08 自定义指令的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段6——CSS选择器

VSCode自定义代码片段(vue主模板)

VSCode自定义代码片段——声明函数

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段——git命令操作一个完整流程

VSCode自定义代码片段8——声明函数