Vue自定义指令
自定义指令介绍 directives - 对普通 DOM 元素进行底层操作
(1) 自定义指令注册
当页面加载时,该元素将获得焦点 (注意:autofocus
在移动版 Safari 上不工作)。事实上,只要你在打开这个页面后还没点击过任何内容,这个输入框就应当还是处于聚焦状态。现在让我们用指令来实现这个功能:
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
})
如果想注册局部指令,组件中也接受一个 directives
的选项:
directives: {
focus: {
// 指令的定义
inserted: function (el) {
el.focus()
}
}
}
然后你可以在模板中任何元素上使用新的 v-focus
属性,如下:
<input v-focus>
(2) 自定义指令钩子
* bind,inserted,update,componentUpdated,unbind
* 参数 el,binding,vnode,oldvnode
指令定义函数提供了几个钩子函数(可选):
bind: 只调用一次,指令第一次绑定到元素时调用。用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
inserted: 被绑定元素插入父节点时调用(父节点存在即可调用)。
update: 所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
componentUpdated: 指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind: 只调用一次, 指令与元素解绑时调用。
钩子函数的参数:(el, binding, vnode, oldVnode)
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.a.b,modifiers的值为{‘a’:true,‘b’:true}
vnode:Vue编译的生成虚拟节点;
oldVnode:上一次的虚拟节点,仅在update和componentUpdated钩子函数中可用。
(3) 函数简写
大多数情况下,我们可能想在 bind 和 update 钩子上做重复动作,并且不想关心其它的钩子函数。可以这样写:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
实例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<p v-color="'yellow'">test123</p>
<!--点击按钮下面的p标签颜色变为绿色-->
<p><button @click="color='green'">color</button></p>
<p v-color="color">123</p>
</div>
<script src="../base/vue.min.js"></script>
<script >
// Vue.directive("color",{
// bind:function(el,binding,vnode){
// el.style.background=binding.value
// },
// update:function(el,binding,vnode){
// el.style.background=binding.value
// }
// })
//简写
Vue.directive("color",function(el,binding,vnode){
el.style.background=binding.value
})
new Vue({
el:"#app",
data:{
color:"orange"
}
})
</script>
</body>
</html>
(4) 对象字面量
如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法类型的 Javascript 表达式。
<div v-demo="{ color: 'white', text: 'hello!' }"></div>
Vue.directive('demo', function (el, binding) {
console.log(binding.value.color) // => "white"
console.log(binding.value.text) // => "hello!"
})
(5) 自定义指令-轮播
inserted 插入最后一个元素时初始化swiper
实例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!--引入是wiper.css样式-->
<link rel="stylesheet" href="./base/swiper.min.css">
<style>
.swipe{
height:300px;
}
</style>
</head>
<body>
<div id="box">
<div class="swiper-container swipe">
<div class="swiper-wrapper">
<!--内部数据datalist发生变化了,然后生成3个swiper-slide真实dom,插入到swiper-wrapper里面去-->
<div
class="swiper-slide"
v-for="(data,index) in datalist"
v-swiper:swipe="{current:index,length:datalist.length,swipe:'swipe'}"
>
{{data}}
</div>
</div>
<!--分页器-->
<div class="swiper-pagination"></div>
</div>
</div>
<!--引入swiper or vue插件-->
<script src="./base/swiper.min.js"></script>
<script src="./base/vue.js"></script>
<script>
//有很多的插件,其实只能实例化一次,
//swiper实例化了多次之后,其实还是可以进行轮播效果,但是我们发现分页器效果已经失效了。
//需求:封装一个指令v-swiper,实现内部帮助我们实例化轮播图的功能
//问题:因为有三个swiper-slide的真实dom生成然后插入,所以new Swiper实例化了3次===>导致了分页器不管用了
//解决? 等最后一个元素插入完成后再去实例化不就ok了吗?怎么样是代表最后一个元素插入了
//那么获取到最后一个插入的元素的下标 === 传入的datalist的数组的长度-1
//自定义指令 v-swiper
Vue.directive("swiper",{
inserted(el,binding,vnode){
// 这个地方是通过vnode.context获取到vue实例,然后就可以通过这个实例.datalist.length获取到数组的长度了
// console.log(vnode.context.datalist.length)
//代表最后一个元素插入完毕了,再进进行一次实例化操作就可以了
if(binding.value.current === binding.value.length-1){
//如果外部 {current,length,swipe:'swipe'}
//new Swiper("."+binding.value.swipe,{.....})
new Swiper("."+binding.arg,{ //默认swiper执行3次,因为插入了3个swiper-slide的真实dom
loop:true,
pagination:{
el:".swiper-pagination"
}
})
}
}
})
new Vue({
el:"#box",
data:{
datalist:[] //定义了初始化的数据
},
// updated(){
// new Swiper(".swipe",{
// loop:true,
// pagination:{
// el:".swiper-pagination"
// }
// })
// },
created(){
setTimeout(() => {
this.datalist = ["111","222","333"] //更改数据
// this.$nextTick(()=>{
// new Swiper(".swipe",{
// loop:true,
// pagination:{
// el:".swiper-pagination"
// }
// })
// })
}, 3000);
}
})
</script>
</body>
</html>
自定义指令钩子函数的案例展示:
<div id="app">
<my-comp v-if="msg" :msg="msg"></my-comp>
<button @click="update">更新</button>
<button @click="uninstall">卸载</button>
<button @click="install">安装</button>
</div>
<script type="text/javascript">
Vue.directive('hello', {
bind: function (el){
console.log('bind');
},
inserted: function (el){
console.log('inserted');
},
update: function (el){
console.log('update');
},
componentUpdated: function (el){
console.log('componentUpdated');
},
unbind: function (el){
console.log('unbind');
}
});
var myComp = {
template: '<h1 v-hello>{{msg}}</h1>',
props: {
msg: String
}
}
new Vue({
el: '#app',
data: {
msg: 'Hello'
},
components: {
myComp: myComp
},
methods: {
update: function (){
this.msg = 'Hi';
},
uninstall: function (){
this.msg = '';
},
install: function (){
this.msg = 'Hello';
}
}
})
</script>
a、页面加载时:bind inserted
b、更新组件:update componentUpdated
c、卸载组件:unbind
d、重新安装组件:bind inserted
注意区别:
bind与inserted:bind时父节点为null,inserted时父节点存在;
update与componentUpdated:update是数据更新前,componentUpdated是数据更新后。