目录
基本用法
自定义指令的选项是由几个钩子函数组成的,每个都是可选的。
-
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{
125px;
}
button{
display: block;
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{
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__;
}
});