vue自定义指令:提高开发效率的秘密武器

前言

vue 中,自定义指令是一种非常有用的功能,可以让我们轻松地扩展 vue 的行为。本文将介绍 vue 自定义指令的相关知识,帮助你更好地掌握 vue 的技能,让你的 vue 应用更加强大和灵活。


在这里插入图片描述

其实说到 vue 中的自定义指令,相信大家都不陌生。在官网中是这么说的,除了核心功能默认内置的指令 (v-modelv-show),vue 也允许注册自定义指令。那什么时候会用到自定义指令呢?代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 dom 元素进行底层操作,这时候就会用到自定义指令。


自定义指令

自定义指令分为:全局自定义指令,局部自定义指令。它有两个参数:参数1:指令的名称;参数2:是一个对象,这个对象身上,有钩子函数。

全局自定义指令

通过 Vue.directive() 函数注册一个全局的指令

// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})

main.js 中引入

import focus from '@/utils/focus'

局部自定义指令

通过组件的 directives 属性,对该组件添加一个局部的指令

directives: {
  focus: {
    // 指令的定义
    inserted: function (el) {
      el.focus()
    }
  }
}

然后你可以在组件中任何元素上使用 v-focus 自定义的指令,如下:

<input v-focus/>

批量注册指令

src 目录下新建文件夹(directives/index.js)管理各个子文件(自定义指令文件),然后在根文件(index.js)中对外暴露 api,最后在 main.js 中引入并注册,这样做的好处是统一管理后,后期比较好维护,而且会比较清晰。

文件目录: src/directives/index.js

import Vue from 'vue';
import copy from './modules/copy';//引入自定义指令
const directives = {
    // 自定义指令
    copy,
};
export default {
    install(Vue) {
        Object.keys(directives).forEach(key => Vue.directive(key, directives[key]))
    }
}

main.js 中引入并注册

import Directives from '@/directives';
Vue.use(Directives);

钩子函数

bind

只调用一次,指令第一次绑定到元素时调用。

inserted

被绑定元素插入父节点时调用。

update

所在组件的虚拟节点更新时调用。

componentUpdated

所在组件的虚拟节点及其子虚拟节点全部更新时调用。

unbind

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


实例

看到这里,你可能对自定义指令已经有了大概的了解和认识,那可能有的同学要问了,在实际的开发中,自定义指令有什么使用场景吗?下面分享在开发中常用的使用场景。


1. 复制粘贴指令

文件目录:src/directives/modules/copy.js

export default {
    bind(el, {
        value
    }) {
        el.$value = value;
        el.handler = () => {
            if (!el.$value) {
                // 值为空的时候,给出提示
                console.log('无复制内容');
                return;
            }
            const textarea = document.createElement('textarea');
            // 将该textarea设为readonly,防止iOS下自动唤起软键盘
            textarea.readOnly = 'readonly';
            textarea.style.position = 'absolute';
            textarea.style.left = '-9999px';
            // 将要copy的值赋给textarea标签的value属性
            textarea.value = el.$value;
            // 将textarea插入到body中
            document.body.appendChild(textarea);
            // 选中值并复制
            textarea.select();
            const result = document.execCommand('Copy');
            if (result) {
                console.log('复制成功');
            }
            // 赋值成功后,将textarea移除掉
            document.body.removeChild(textarea);
        };
        // 绑定点击事件
        el.addEventListener('click', el.handler);
    },
    componentUpdated(el, {
        value
    }) {
        el.$value = value;
    },
    // 指令与元素解绑的时候,移除事件绑定
    unbind(el) {
        el.removeEventListener('click', el.handler);
    }
}

组件中使用

<template>
  <div>
    <button v-copy="copyText">复制</button>
  </div>
</template>
<script>
export default {
  data() {
    return {
      copyText: "等待被复制的内容",
    };
  },
};
</script>

实现效果

在这里插入图片描述


2. 长按指令

文件目录:src/directives/modules/longpress.js

export default {
    bind(el, {
        value
    }) {
        if ('function' !== typeof value) {
            throw 'callback must be a function';
        }
        let pressTimer = null;
        let start = e => {
            if ('click' === e.type && 0 !== e.button) {
                return;
            }
            if (null === pressTimer) {
                pressTimer = setTimeout(() => {
                    // 执行函数
                    value(e);
                }, 1000);
            }
        };
        let cancel = e => {
            if (null !== pressTimer) {
                clearTimeout(pressTimer);
                pressTimer = null;
            }
        };
        // 添加事件监听器
        el.addEventListener('mousedown', start);
        el.addEventListener('mouseout', cancel);
        el.addEventListener('click', cancel);
        el.addEventListener('touchstart', start);
        el.addEventListener('touchend', cancel);
        el.addEventListener('touchcancel', cancel);
    }
}

组件中使用

<template>
  <div>
    <button v-longpress="longpress">长按我</button>
  </div>
</template>
<script>
export default {
  methods: {
    longpress() {
      alert("触发了长按");
    },
  },
};
</script>

实现效果

在这里插入图片描述


3. 输入框防抖指令

文件目录:src/directives/modules/debounce.js

export default {
    inserted(el, {
        value
    }) {
        if ('function' !== typeof value) {
            throw 'directive value must be function';
        }
        let timer;
        el.addEventListener('keyup', () => {
            timer && clearTimeout(timer);
            timer = setTimeout(() => {
                value && value();
            }, 1000);
        });
    }
}

组件中使用

<template>
  <div>
    <button v-longpress="longpress">长按我</button>
  </div>
</template>
<script>
export default {
  methods: {
    longpress() {
      alert("触发了长按");
    },
  },
};
</script>

实现效果

在这里插入图片描述


4. 输入框自动聚焦指令

文件目录:src/directives/modules/focus.js

export default {
    inserted(el, {
        value
    }) {
        // 聚焦元素
        el.focus()
    }
}

组件中使用

<template>
  <div>
    <input v-focus type="text" />
  </div>
</template>

实现效果

在这里插入图片描述


5. 全屏指令

文件目录:src/directives/modules/fullScreen.js

export default {
    bind(el, binding) {
        if (binding.modifiers.icon) {
            if (el.hasIcon) return
            // 创建全屏图标
            const iconElement = document.createElement('i')
            iconElement.setAttribute('class', 'el-icon-full-screen')
            iconElement.setAttribute('style', 'margin-left:5px')
            el.appendChild(iconElement)
            el.hasIcon = true
        }
        el.style.cursor = el.style.cursor || 'pointer'
        // 监听点击全屏事件
        el.addEventListener('click', () => handleClick())
    }
}

function handleClick() {
    let FullScreen = false
    let element = document.documentElement;
    if (FullScreen) {
        if (document.exitFullscreen) {
            document.exitFullscreen();
        } else if (document.webkitCancelFullScreen) {
            document.webkitCancelFullScreen();
        } else if (document.mozCancelFullScreen) {
            document.mozCancelFullScreen();
        } else if (document.msExitFullscreen) {
            document.msExitFullscreen();
        }
    } else {
        if (element.requestFullscreen) {
            element.requestFullscreen();
        } else if (element.webkitRequestFullScreen) {
            element.webkitRequestFullScreen();
        } else if (element.mozRequestFullScreen) {
            element.mozRequestFullScreen();
        } else if (element.msRequestFullscreen) {
            element.msRequestFullscreen();
        }
    }
    FullScreen = !FullScreen;
}

组件中使用

<template>
  <div>
    <span v-fullScreen.icon>全屏</span>
  </div>
</template>

实现效果

在这里插入图片描述


6. 文字超出展示省略号指令

文件目录:src/directives/modules/ellipsis.js

export default {
    bind(el, binding) {
        console.log(el,binding);
        el.style.width = binding.arg || 100 + 'vw'
        el.style.whiteSpace = 'nowrap'
        el.style.overflow = 'hidden';
        el.style.textOverflow = 'ellipsis';
    }
}

组件中使用

<template>
  <div>
    <div v-ellipsis>给你一首诗的时间,坐在炉火边,捧着诗卷阅读,听不见大摆钟的摇摆,远离岁月的尘埃</div>
  </div>
</template>

实现效果

在这里插入图片描述


7. 拖拽指令

文件目录:src/directives/modules/drag.js

export default {
    bind(el, binding) {
        document.onselectstart = function () {
            return false //禁止选择网页上的文字
        }
        el.style.cursor = 'move' // 图标
        el.style.position = 'absolute'
        el.onmousedown = function (ev) {
            // 用元素距离视窗的X、Y坐标,减去el元素最近的相对定位父元素的left、top值
            var sX = ev.clientX - el.offsetLeft
            var sY = ev.clientY - el.offsetTop
            document.onmousemove = function (ev) {
                var eX = ev.clientX - sX
                var eY = ev.clientY - sY
                // 不断地更新元素的left、top值
                el.style.left = eX + 'px'
                el.style.top = eY + 'px'
            }
            document.onmouseup = function () {
                // 清除mousemove事件
                document.onmousemove = null
            }
        }

    }
}

组件中使用

<template>
  <div>
    <div class="box" v-drag>拖拽</div>
  </div>
</template>
<script>
export default {
  data() {
    return {};
  },
};
</script>
<style scoped>
.box {
  width: 100px;
  height: 100px;
  background: cadetblue;
  text-align: center;
  line-height: 100px;
  color: #fff;
}
</style>

实现效果

在这里插入图片描述


8. 字符串整形指令

文件目录:src/directives/modules/format.js

export default {
    bind(el, binding, vnode) {
        const {
            value,
            modifiers
        } = binding
        if (!value) return
        let formatValue = value
        if (modifiers.toFixed) {
            formatValue = value.toFixed(2)
        }
        console.log(formatValue)
        if (modifiers.price) {
            formatValue = formatNumber(formatValue)
        }
        el.innerText = formatValue
    },
}

function formatNumber(num) {
    num += '';
    let strs = num.split('.');
    let x1 = strs[0];
    let x2 = strs.length > 1 ? '.' + strs[1] : '';
    var rgx = /(\d+)(\d{3})/;
    while (rgx.test(x1)) {
        x1 = x1.replace(rgx, '$1' + ',' + '$2');
    }
    return x1 + x2
}

组件中使用

<template>
  <div>
    <div v-format.toFixed.price="123456789"></div>
    <div v-format.toFixed="123.4567"></div>
  </div>
</template>

实现效果

在这里插入图片描述

持续更新中...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水星记_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值