vue|自定义指令可以做的事情❣️❣️

246 阅读3分钟

1、场景

对底层的DOM进行操作的时候,会用到自定义指令

2、注册指令分为全局和局部

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()  // 页面加载完成之后自动让输入框获取到焦点的小功能
  }
})
// 注册一个局部自定义指令 `v-focus`
directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus() // 页面加载完成之后自动让输入框获取到焦点的小功能
    }
  }
}
<input v-focus />

4、自定义指令的钩子函数

bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置

inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)

update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新

componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用

unbind:只调用一次,指令与元素解绑时调用

5、钩子函数的参数

el:指令所绑定的元素,可以用来直接操作 DOM

binding:一个对象,包含以下 property:

`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 编译生成的虚拟节点

`oldVnode`:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用

6、常用案例

1. 防抖
2. input聚焦
3. 图片懒加载
4. 一键copy
5. 拖曳
6. 下拉菜单
7. 相对时间转换

7、input聚焦

//全局注册
<body>
    <div id="app">
        <input type="text" v-focus>
    </div>
    <script>
        Vue.directive('focus', {
            // inserted 是钩子函数 每一个钩子函数都有2个参数 el,binding
            inserted: function (el) {
                el.focus();
            }
        })
        var app = new Vue({
            el: '#app',
        })
    </script>
</body>
//局部注册
<body>
    <div id="app">
        <input type="text" v-focus>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            // 自定义指令
            directives: {
                // 自定义指令的名称-v-focus
                focus: {
                    // 钩子函数
                    inserted: function (el) {
                        el.focus();

                    }
                }
            }
        })
    </script>
</body>

image (15).png

8、 防抖

自定义指令按照传参的方式执行

<body>
    <div id="app">
        <button v-debounce="{ev:'click',fn:sayHello,delay:1000}">提交</button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            directives: {
                debounce: {
                    bind: function (el, binding) {
                       // 参数解构
                        let { ev, fn, delay } = binding.value;
                        let timer;
                        el.addEventListener(ev, () => {
                            if (timer) {
                                clearTimeout(timer);
                            }
                            timer = setTimeout(() => {
                                fn();
                            }, delay)
                        })
                    }
                }
            },
            methods: {
                sayHello() {
                    console.log('hello!')
                }
            },
        })
    </script>
</body>

频繁点击,只有最后一次触发

image (16).png

9、时间转换

        Vue.directive('time', {
            bind: function (el, binding) {
                el.innerHTML = Time.getFormatTime(binding.value);
                // 定时器 el.__timeout__ 每分钟触发一次,更新时间
                el.__timeout__ = setInterval(function () {
                    // 将格式化时间写入指令所在元素
                    el.innerHTML = Time.getFormatTime(binding.value);
                }, 60000); // 注意这里的60000是毫秒
            },
            unbind: function (el) {
                // 在 unbind 钩子里清除定时器。
                clearInterval(el.__timeout__);
                delete el.__timeout__;
            }
        })

image (17).png

10、自己实现v-if指令

实现原理是通过注释节点,注释节点不会在DOM中渲染


    kif: {
        inserted: function (el, binding, vnode) {
            // 如果v-if的值是true,代表要隐藏元素
            if (binding.value) {
                //  创建一个注释节点
                let comment = document.createComment(' ')
                Object.defineProperty(comment, 'setAttribute', {
                    //设置值是空
                    value: () => undefined
                })
                // ================操作当前节点的各属性
                vnode.elm = comment
                vnode.text = ' '
                vnode.isComment = true
                vnode.context = undefined
                vnode.tag = undefined
                vnode.data.directives = undefined
                // 组件实例存在
                if (vnode.componentInstance) {
                    vode.componentInstance.$el = comment;
                }
                // ==================父节点替换
                if (el.parentNode) {
                    el.parentNode.replaceChild(comment, el)
                }

            }
        }
    }
}

通过给标签设置v-kif="false" 和 v-kif="true",对比一下,vnode的区别

image.png

通过F12查看elements可以看到我们实现的注释节点

image.png

参考👀

# vue中如何自定义指令

# Vue进阶(四)自定义指令,并使用自定义指令实现防抖节流

# VUE 用自定义指令实现v-if的效果,不是display:none