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。

image-20220422015300436

注意事项

全局自定义指令

上面讲的都是局部自定义指令,可以写成全局自定义指令

<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>

这样写会报错

image-20220422020200771

可以统一改成小写但是不符合命名规范,因此采用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命名,并且需要用引号包裹
posted @ 2022-04-22 02:10  GoodForNothing  阅读(47)  评论(0编辑  收藏  举报
//看板娘