提示
本文主要讲解 Vue 中自定义指令。@ermo
# 自定义指令
# 简介
v-show
和 v-bind
这些都是 Vue.js 的内置指令。如果需要对普通 DOM 元素进行底层操作就需要用到自定义指令。
下例子将自定义一个 v-focus
用于进行元素聚焦:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
</head>
<body>
<div id="app">
<input v-focus>
</div>
<script>
Vue.directive('focus', {
// 当被绑定元素被插入到 DOM 时
inserted: function(el) {
// 聚焦元素
el.focus();
}
});
new Vue({
el: '#app'
});
</script>
</body>
</html>
也可以针对一个 Vue 实例自定义指令,这样的指令只能被当前实例使用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
</head>
<body>
<div id="app">
<input type="text" v-focus>
</div>
<script>
new Vue({
el: '#app',
directives: {
focus: {
inserted: function(el) {
el.focus();
}
}
}
});
</script>
</body>
</html>
# 钩子函数
一个指令可以提供以下钩子函数:
bind
:只调用一次,指令第一次绑定到元素时调用。这里可以进行元素初始化。inserted
:被绑定元素插入到父节点时使用。update
:所在组件的 VNode 更新时调用。可以通过比较更新前后的值来忽略不必要的模板更新。componentUpdated
:指令所在组件的 VNode 及其子 VNode 全部更新后调用。unbind
:只调用一次,指令与元素解绑时调用。
钩子函数包含以下参数:
el
:指令绑定的元素,可直接操作 DOM。binding
:指令名,不包括v-
前缀。value
:指令绑定的值。oldValue
:指令绑定的前一个值,仅在update
和componentUpdated
钩子中可用。expression
:字符串形式的指令表达式。arg
:传给指令的参数,可选。modifiers
:一个包含修饰符的对象。
vnode
:Vue 编译生成的虚拟节点。oldVnode
:上一个虚拟节点。仅在update
和componentUpdated
钩子中可用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
</head>
<body>
<div id="app" v-ermo:hello.a.b="message">
</div>
<script>
Vue.directive('ermo', {
bind: function(el, binding, vnode) {
var s = JSON.stringify;
el.innerHTML =
'name: ' + s(binding.name) + '<br>' +
'value: ' + s(binding.value) + '<br>' +
'expression: ' + s(binding.expression) + '<br>' +
'argument: ' + s(binding.arg) + '<br>' +
'modifiers: ' + s(binding.modifiers) + '<br>' +
'vnode keys: ' + Object.keys(vnode) + '<br>'
;
}
});
new Vue({
el: '#app',
data: {
message: 'hello'
}
});
</script>
</body>
</html>
上例在页面的返回:
name: "ermo"
value: "hello"
expression: "message"
argument: "hello"
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
# 动态参数
指令的参数可以是动态的,语法为 v-mydirective:[arg]="value"
,arg
参数可以根据组件实例数据进行更新。
下例中,自定义一个指令 v-pin
,使用的元素可以固定到距顶部 200px 位置:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
</head>
<body>
<div id="app">
<p>向下滑动网页</p>
<p v-pin="200">当前内容相对顶部固定在200px位置</p>
</div>
<script>
Vue.directive('pin', {
bind: function(el, binding, vnode) {
el.style.position = 'fixed'
el.style.top = binding.value + 'px'
}
})
new Vue({
el: '#app'
})
</script>
</body>
</html>
如果希望固定的位置是从左侧计算而不是顶部,就需要用到动态参数:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
</head>
<body>
<div id="app">
<p>向下滚动网页</p>
<p v-pin:[direction]="200">固定内容</p>
</div>
<script>
Vue.directive('pin', {
bind: function(el, binding, vnode) {
el.style.position = 'fixed'
if (binding.arg === 'left') {
el.style.left = binding.value + 'px'
} else {
el.style.top = binding.value + 'px'
}
}
})
new Vue({
el: '#app',
data: function() {
return {
direction: 'left'
}
}
})
</script>
</body>
</html>
# 函数简写
有时间,想在 bind
和 update
时触发相同行为,而不关心其他钩子函数。可以使用简写方式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
</head>
<body>
<div id="app">
<p v-color="bgColor">一段文字</p>
</div>
<script>
Vue.directive('color', function(el, binding) {
el.style.backgroundColor = binding.value
})
new Vue({
el: '#app',
data: {
bgColor: 'skyblue'
}
})
</script>
</body>
</html>
# 对象字面量
如果自定义指令需要传入多个值,可以使用对象字面量,对象字面量必须是一个标准的 JavaScript 对象:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
</head>
<body>
<div id="app">
<p v-color="{bgColor: 'skyblue', fontColor: 'pink'}">一段文字</p>
</div>
<script>
Vue.directive('color', function(el, binding) {
el.style.color = binding.value.fontColor
el.style.backgroundColor = binding.value.bgColor
})
new Vue({
el: '#app'
})
</script>
</body>
</html>