除了核心功能默认内置的指令 (v-model
和 v-show
),Vue 也允许注册自定义指令。注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。举个聚焦输入框的例子,如下:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
然后你可以在模板中任何元素上使用新的 v-focus
属性,如下:
|
钩子函数
bind
:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
inserted
:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
update
:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind
:只调用一次,指令与元素解绑时调用
接下来我们来看一下钩子函数的参数 (即 el
、binding
、vnode
和 oldVnode
)。
el
:指令所绑定的元素,可以用来直接操作 DOM 。binding
:一个对象,包含以下属性:name
:指令名,不包括v-
前缀。value
:指令的绑定值,例如:v-my-directive="1 + 1"
中,绑定值为2
。oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。无论值是否改变都可用。expression
:字符串形式的指令表达式。例如v-my-directive="1 + 1"
中,表达式为"1 + 1"
。arg
:传给指令的参数,可选。例如v-my-directive:foo
中,参数为"foo"
。modifiers
:一个包含修饰符的对象。例如:v-my-directive.foo.bar
中,修饰符对象为{ foo: true, bar: true }
。
vnode
:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。oldVnode
:上一个虚拟节点,仅在update
和componentUpdated
钩子中可用。
举两个实际的例子:
图片的懒加载,默认在图片加载之前给容器设置一个随机色,然后图片加载完以后赋值给容器的背景图片(或者给容器里面的img的src赋值)
<style>
.imgwraps{
height: 200px;
width: 200px;
background-position: center center;
}
</style>
<div id="app">
<div class="imgwraps" v-for="item in lists" v-img="item.url">
<!-- <img src="" alt=""> -->
</div>
</div>
<script>
Vue.directive('img',{
inserted:function(el,binding){
var color=Math.floor(Math.random()*1000000);
// var target=el.getElementsByTagName('img')[0];
el.style.background='#'+color; // 设置随机背景色
var img= new Image();
img.src=binding.value; // 获取绑定到指令上面的值
img.onload=function(){
el.style.backgroundImage="url("+ binding.value +")";
// target.src=binding.value;
}
}
})
new Vue({
el:'#app',
data:{
lists:[
{url:'1.jpg'},
{url:'2.jpg'},
{url:'3.jpg'}
]
}
})
</script>
用自定义指令实现一个常见的 tip 提示弹框的功能,鼠标移入元素,可以在元素的上下左右显示 tip,鼠标移出则隐藏 tip
<style>
li{
list-style: none;
width: 100px;
height: 100px;
border:1px solid #eee;
position: relative;
}
li:hover{
background: red;
}
.tips{
position: absolute;
width: 100px;
height: 50px;
background: #000;
color: #fff;
text-align: center;
}
.tips.left{
left: 130px;
top: 25px;
}
.tips.right{
right: 130px;
top: 25px;
}
.tips.top{
left: 50%;
margin-left: -50px;
top: -75px;
}
.tips.bottom{
left: 50%;
margin-left: -50px;
bottom: -75px;
}
.tips:after{
position: absolute;
border:10px solid transparent;
content: "";
}
.left:after{
border-right-color: #000;
left: -20px;
top: 50%;
margin-top: -10px;
}
.right:after{
border-left-color: #000;
right: -20px;
top: 50%;
margin-top: -10px;
}
.bottom:after{
border-bottom-color: #000;
top: -20px;
left: 50%;
margin-left: -10px;
}
.top:after{
border-top-color: #000;
bottom: -20px;
left: 50%;
margin-left: -10px;
}</style>
<div id="app">
<ul>
<!-- 可支持设置top,bottom,left,right四个方向 -->
<li v-mytip.top="mes">sss</li>
</ul>
</div>
<script>
Vue.directive('mytip', {
bind: function (el, binding, vnode) {
var flag=false;
el.onmouseover=function(){
if (!flag) {
var newEle=document.createElement("div");
newEle.id="tip";
newEle.className='tips '+Object.keys(binding.modifiers); // 一个包含修饰符的对象,这里主要用获取tops的展示
newEle.innerHTML=binding.expression; //字符串形式的指令表达式
el.append(newEle);
flag=true;
}
}
el.onmouseout=function(){
document.getElementById('tip').remove(); flag=false;
}
}
})
new Vue({ el:'#app', data:{ mes:'hahahahah' } }) </script>
select指令
<article>
<h1 class="header red">Vue.js Custom Directive with Semantic-UI Dropdown</h1>
<div class="ui divider"></div>
<div id="el">
<select v-select="selected">
<option value="">default</option>
<option v-for="option in options" v-bind:value="option.id">
{{ option.text }}
</option>
</select>
<div class="ui divider"></div>
<span>Selected Key : {{ selected }}</span>
</div>
</article>
Vue.directive('select', {
twoWay: true,
priority: 1000,
params: ['options'],
bind: function () {
var self = this
$(this.el)
.dropdown({
onChange: function(value) {
//alert(value)
self.set(this.value)
}
})
},
update: function (value) {
console.log(value);
}
})
var vm = new Vue({
el: '#el',
data: {
selected: 0,
options: [
{ id: 1, text: 'Hello' },
{ id: 2, text: 'Bonjour' },
{ id: 3, text: 'Salam' }
]
},
watch: {
'selected': function (val, oldVal) {
//alert('Parent --> new value is :'+ this.options[val-1].text);
}
}
})
最大的字符数限制的指令
<div id="root" class="container">
<h2>You can type only 10 characters</h2>
<textarea v-maxchars="10" cols="50" rows="5"></textarea>
</div>
var maxchars = Vue.directive("maxchars", {
bind: function(el, binding, vnode) {
var max_chars = binding.expression;
var handler = function(e) {
if (e.target.value.length > max_chars) {
e.target.value = e.target.value.substr(0, max_chars);
}
};
el.addEventListener("input", handler);
}
});
var app = new Vue({
el: '#root'
});