16.自定义指令
函数式
通过一个例子来讲解函数式自定义指令
按按钮n加1,同时显示放大十倍后的n。
<body>
<div id="root">
<h2>我是:<span v-text="n"></span></h2>
<h2 >我是十倍的:<span v-big="n"></span></h2>
<button @click="n++">点我+1</button>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
n:1
},
directives:{
big(element,binding){
element.innerText = binding.value * 10
}
}
})
</script>
通过在directives中创建函数来实现函数式自定义指令,传入的参数第一个是当前的节点,第二个是绑定的值。
在两种地方这里的指令中的函数会被调用
-
指令与元素成功绑定时(一上来)。
-
指令所在的模板被重新解析时。
对象式
对象式的自定义指令是对函数式的一种补充,因为函数式有缺陷。
需求:
每次点击按钮,input输入框中的数据为n的十倍,同时获取焦点
<body>
<div id="root">
<h2>{{n}}</h2>
<button @click="n++">点我+1</button>
<input type="text" v-fbind="n">
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
n:1
},
directives:{
fbind(element,binding) {
element.value = binding.value
element.focus()
}
}
})
</script>
每次点击都会显示n的十倍,但是初始的焦点不在输入框中。
从原始的js来解释这个问题
<!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>
<style>
.demo{
background-color: orange;
}
</style>
</head>
<body>
<button id="btn">点我创建输入框</button>
<script>
const btn = document.getElementById('btn')
btn.onclick = () => {
const input = document.createElement("input")
input.className = "demo"
input.value = 100
console.log(input.parentElement)
document.body.appendChild(input)
// 放入父节点之后才能生效
input.focus()
console.log(input.parentElement)
}
</script>
</body>
</html>
可以从代码中看到,有些操作放在父节点之后才能生效,例如获取焦点、改变父节点属性等需要对父节点DOM的操作需要在子节点放入父节点之后操作。
而我们的函数式自定义指令只有在绑定和重新解析的时候执行,在第一次绑定的时候,节点还没有放入DOM中,因此第一次焦点不在输入框。
而对象式可以解决这类问题
<body>
<div id="root">
<h2>{{n}}</h2>
<button @click="n++">点我+1</button>
<input type="text" v-fbind="n">
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
n:1
},
directives:{
fbind:{
bind(element,binding){
console.log("bind")
element.value = binding.value * 10
},
inserted(element,binding){
console.log("inserted")
element.focus()
},
update(element,binding){
console.log("update")
element.value = binding.value * 10
element.focus()
}
}
}
})
</script>
对象式有三个函数分别是bind、inserted、update。
- bind是对数据和节点进行绑定的时候调用
- inserted是将节点插入父节点时调用
- update模型更新的时候调用
因此通过在inserted之后获取焦点可以实现第一次进入获取焦点。
函数式相当于简化的对象式,函数式中的函数其实代表了bind和update。
注意事项
全局自定义指令
上面讲的都是局部自定义指令,可以写成全局自定义指令
<script>
VUE.directive('fbind',{
bind(element,binding){
console.log("bind")
element.value = binding.value * 10
},
inserted(element,binding){
console.log("inserted")
element.focus()
},
update(element,binding){
console.log("update")
element.value = binding.value * 10
element.focus()
}
})
VUE.directive('fbind2',function(element, binding){
element.value = binding.value * 10
element.focus()
})
</script>
大小写问题
不能写成bigName这种形式,因为VUE指令无法识别大小写
<body>
<div id="root">
<h2>我是:<span v-text="n"></span></h2>
<h2 >我是十倍的:<span v-bigNumber="n"></span></h2>
<button @click="n++">点我+1</button>
</div>
</body>
<script>
Vue.config.productionTip = false
new Vue({
el:"#root",
data:{
n:1
},
directives:{
bigNumber(element,binding){
element.innerText = binding.value * 10
}
}
})
</script>
这样写会报错
可以统一改成小写但是不符合命名规范,因此采用kebab-case命名方式,也就是用-来分割单词big-number,而不能采用驼峰命名法,如果采用big-number命名,不能用简写形式,必须要在指令名上加引号。
'bigNumber':function(element,binding){
element.innerText = binding.value * 10
}
'fbind':{
bind(element,binding){
element.value = binding.value * 10
element.focus()
},
inserted(element,binding){
},
update(element,binding){
element.value = binding.value * 10
element.focus()
}
}
总结
语法
-
局部指令
//对象式 new Vue({ directives:{指令名:配置对象} }) // 函数式 new Vue({ directives{指令名:回调函数} })
-
全局指令
- Vue.directive(指令名,配置对象) 或 Vue.directive(指令名,回调函数)
对象式三个回调
- bind是对数据和节点进行绑定的时候调用
- inserted是将节点插入父节点时调用
- update模型更新的时候调用
注意事项
- 指令定义时不加v-,但使用时要加v-
- 指令名如果是多个单词,要使用kebab-case命名方式,不要用camelCase命名,并且需要用引号包裹