超实用:Vue 自定义指令合集

开发 前端
对于在项目中常用到的指令,在此做了一个合集介绍,附源码可以直接在项目中使用。

 [[409346]]

在 Vue2.0 中,代码复用和抽象的主要形式是组件。然而,有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。

你可以将一些 css 样式抽象到指令中,也可以将一些 js 操作放到指令中去执行。就使用上来说,指令不用像组件一样需要引入和注册,注册后使用非常简洁方便。

对于在项目中常用到的指令,在此做了一个合集介绍,附源码可以直接在项目中使用。

元素点击范围扩展指令 v-expandClick

使用该指令可以隐式的扩展元素的点击范围,由于借用伪元素实现,故不会影响元素在页面上的排列布局。

可传入的参数为:上右下左扩展的范围,单位 px,默认向外扩展 10px。指令的代码如下: 

  1. export default function (el, binding) {  
  2.     const s = document.styleSheets[document.styleSheets.length - 1]  
  3.     const DEFAULT = -10 // 默认向外扩展10px  
  4.     const ruleStr = `content:"";position:absolute;top:-${top || DEFAULT}px;bottom:-${bottom || DEFAULT}px;right:-${right || DEFAULT}px;left:-${left || DEFAULT}px;` 
  5.     const [top, right, bottom, left] = binding.expression && binding.expression.split(',') || [] 
  6.     const classNameList = el.className.split(' ') 
  7.     el.className = classNameList.includes('expand_click_range') ? classNameList.join(' ') : [...classNameList, 'expand_click_range'].join(' ') 
  8.     elel.style.position = el.style.position || "relative" 
  9.     if (s.insertRule) {  
  10.         s.insertRule('.expand_click_range::before' + '{' + ruleStr + '}', s.cssRules.length)  
  11.     } else { /* IE */  
  12.         s.addRule('.expand_click_range::before', ruleStr, -1)  
  13.     }  

参数 Attributes:

参数 说明 默认值 类型 可选
top, right, bottom, left 上右下左扩展宽度(逗号分割),      
单位px 10,10,10,10 String 可填  

然后你可以在模板中任何元素上使用新的 v-expandClick property,如下: 

  1. <div v-expandClick="20,30,40,50" @click="glabClickoutside"> 点击范围扩大</div> 

文本内容复制指令 v-copy

使用该指令可以复制元素的文本内容(指令支持单击复制 v-copy、双击复制 v-copy.dblclick、点击icon复制 v-copy.icon 三种模式),不传参数时,默认使用单击复制。

指令的代码如下: 

  1. export default {  
  2.   bind (el, binding) {  
  3.     // 双击触发复制  
  4.     if (binding.modifiers.dblclick) {  
  5.       el.addEventListener('dblclick', () => handleClick(el.innerText))  
  6.       el.style.cursor = 'copy'  
  7.     }  
  8.     // 点击icon触发复制  
  9.     else if (binding.modifiers.icon) {  
  10.       if (el.hasIcon) return  
  11.       const iconElement = document.createElement('i')  
  12.       iconElement.setAttribute('class', 'el-icon-document-copy')  
  13.       iconElement.setAttribute('style', 'margin-left:5px')  
  14.       el.appendChild(iconElement)  
  15.       el.hasIcon = true  
  16.       iconElement.addEventListener('click', () => handleClick(el.innerText))  
  17.       iconElement.style.cursor = 'copy'  
  18.     }  
  19.     // 单击触发复制  
  20.     else {  
  21.       el.addEventListener('click', () => handleClick(el.innerText))  
  22.       el.style.cursor = 'copy'  
  23.     }  
  24.   }  
  25. function handleClick (text) {  
  26.   // 创建元素  
  27.   if (!document.getElementById('copyTarget')) {  
  28.     const copyTarget = document.createElement('input')  
  29.     copyTarget.setAttribute('style', 'position:fixed;top:0;left:0;opacity:0;z-index:-1000;')  
  30.     copyTarget.setAttribute('id', 'copyTarget')  
  31.     document.body.appendChild(copyTarget)  
  32.   }  
  33.   // 复制内容  
  34.   const input = document.getElementById('copyTarget')  
  35.   input.value = text  
  36.   input.select()  
  37.   document.execCommand('copy') 
  38.   // alert('复制成功')  

参数 Attributes:

参数 说明 默认值 类型 可选
dblclick 双击复制文本内容 / String 可选
icon 单击icon复制文本内容 / String 可选

然后你可以在模板中任何元素上使用新的 v-copy property,如下: 

  1. <div v-copy> 单击复制 </div>  
  2. <div v-copy.dblclick> 双击复制 </div>  
  3. <div v-copy.icon> icon复制 </div> 

元素全屏指令 v-screenfull

全屏指令,点击元素进行全屏/退出全屏的操作。支持元素后面是否插入 element-ui 的全屏图标 el-icon-full-screen。

指令的代码如下: 

  1. import screenfull from 'screenfull'  
  2. export default {  
  3.   bind (el, binding) {  
  4.     if (binding.modifiers.icon) {  
  5.       if (el.hasIcon) return  
  6.       // 创建全屏图标  
  7.       const iconElement = document.createElement('i')  
  8.       iconElement.setAttribute('class', 'el-icon-full-screen')  
  9.       iconElement.setAttribute('style', 'margin-left:5px')  
  10.       el.appendChild(iconElement)  
  11.       el.hasIcon = true  
  12.   }  
  13.     elel.style.cursor = el.style.cursor || 'pointer'  
  14.     // 监听点击全屏事件 
  15.     el.addEventListener('click', () => handleClick())  
  16.   }  
  17. function handleClick () {  
  18.   if (!screenfull.isEnabled) {  
  19.     alert('浏览器不支持全屏')  
  20.     return  
  21.   }  
  22.   screenfull.toggle() 

参数 Attributes:

参数 说明 默认值 类型 可选
icon 是否添加 icon / String 可选

然后你可以在模板中任何元素上使用新的 v-screenfull property,如下:

  1. <div v-screenfull.icon> 全屏 </div> 

元素说明指令 v-tooltip

为元素添加说明,如同 element-ui 的 el-tooltip(问号 icon 在鼠标覆盖后,展示说明文字)。

指令的代码如下: 

  1. import Vue from 'vue'  
  2. export default function (el, binding) {  
  3.     if (el.hasIcon) return  
  4.     const iconElement = structureIcon(binding.arg, binding.value)  
  5.     el.appendChild(iconElement)  
  6.     el.hasIcon = true  
  7.  
  8. function structureIcon (content, attrs) {  
  9.     // 拼接绑定属性  
  10.     let attrStr = ''  
  11.     for (let key in attrs) {  
  12.         attrStr += `${key}=${attrs[key]} `  
  13.     }  
  14.     const a = `<el-tooltip content=${content} ${attrStr}><i class="el-icon-question" style="margin:0 10px"></i></el-tooltip> 
  15.     // 创建构造器  
  16.     const tooltip = Vue.extend({  
  17.         template: a  
  18.     })  
  19.     // 创建一个 tooltip 实例并返回 dom 节点  
  20.     const component = new tooltip().$mount()  
  21.     return component.$el  

参数 Attributes:

参数 说明 默认值 类型 可选
content 传给指令的参数。例如 v-tooltip:content 中,参数为 "content" ,tooltip中展示的内容为:"content" / String 可选
tootipParams element-ui 支持的 tooltip 属性 / Object 可选

然后你可以在模板中任何元素上使用新的 v-tooltip property,如下: 

  1. <div v-tooltip:content='tootipParams'> 提示 </div> 

举例: 

  1. <div v-tooltip:提示内容为XXX1> 提示1</div>  
  2. <div v-tooltip:提示内容为XXX='tootipParams'> 提示2 </div> 

为指令传入 element-ui 支持的参数: 

  1. data() {  
  2.     return {  
  3.         tootipParams: {  
  4.             placement: 'top',  
  5.             effect: 'light',  
  6.         }  
  7.     }  

文字超出省略指令 v-ellipsis

使用该指令当文字内容超出宽度(默认 100 px)时自动变为省略形式。等同于使用 css: 

  1. width: 100px;  
  2. whiteSpace: nowrap  
  3. overflow: hidden;  
  4. textOverflow: ellipsis; 

使用指令效果:

image.png

指令的代码如下: 

  1. export default function (el, binding) {  
  2.     el.style.width = binding.arg || 100 + 'px'  
  3.     el.style.whiteSpace = 'nowrap'  
  4.     el.style.overflow = 'hidden' 
  5.     el.style.textOverflow = 'ellipsis' 

参数 Attributes:

参数 说明 默认值 类型 可选
width 元素宽度 100 Number 必填

然后你可以在模板中任何元素上使用新的 v-ellipsis property,如下: 

  1. <div v-ellipsis:100> 需要省略的文字是阿萨的副本阿萨的副本阿萨的副本阿萨的副本</div> 

回到顶部指令 v-backtop

使用该指令可以让页面或指定元素回到顶部。

可选指定元素,如果不指定则全局页面回到顶部。可选在元素偏移多少 px 后显示 backtop 元素,例如在滚动 400px 后显示回到顶部按钮。

image.png

指令的代码如下: 

  1. export default {  
  2.   bind (el, binding, vnode) {  
  3.     // 响应点击后滚动到元素顶部  
  4.     el.addEventListener('click', () => {  
  5.     const target = binding.arg ? document.getElementById(binding.arg) : window  
  6.     target.scrollTo({  
  7.       top: 0,  
  8.       behavior: 'smooth'  
  9.       })  
  10.     })  
  11.   },  
  12.   update (el, binding, vnode) {  
  13.     // 滚动到达参数值才出现绑定指令的元素  
  14.     const target = binding.arg ? document.getElementById(binding.arg) : window  
  15.     if (binding.value) {  
  16.       target.addEventListener('scroll', (e) => {  
  17.         if (e.srcElement.scrollTop > binding.value) {  
  18.           el.style.visibility = 'unset'  
  19.         } else {  
  20.           el.style.visibility = 'hidden'  
  21.         }  
  22.       })  
  23.     }  
  24.     // 判断初始化状态  
  25.     if (target.scrollTop < binding.value) {  
  26.       el.style.visibility = 'hidden'  
  27.     }  
  28.   },  
  29.   unbind (el) {  
  30.     const target = binding.arg ? document.getElementById(binding.arg) : window  
  31.     target.removeEventListener('scroll')  
  32.     el.removeEventListener('click')  
  33.   }  

参数 Attributes:

参数 说明 默认值 类型 可选
id 给需要回到顶部的元素添加的id / String 可选
offset 偏移距离为 height 时显示指令绑定的元素 / Number 可选

然后你可以在模板中任何元素上使用新的 v-backtop property,如下表示在 id 为 app 的元素滚动 400px 后显示绑定指令的元素: 

  1. <div  v-backtop:app="400"> 回到顶部 </div> 

也可以这样使用,表示为一直显示绑定指令的元素,并且是全局页面回到顶部: 

  1. <div  v-backtop> 回到顶部 </div> 

空状态指令 v-empty

使用该指令可以显示缺省的空状态。可以传入默认图片(可选,默认无图片)、默认文字内容(可选,默认为暂无数据)、以及标示是否显示空状态(必选)。

image.png

指令的代码如下: 

  1. import Vue from "vue";  
  2. export default {  
  3.   update (el, binding, vnode) {  
  4.     elel.style.position = el.style.position || 'relative'  
  5.     const { offsetHeight, offsetWidth } = el  
  6.     const { visible, content, img } = binding.value  
  7.     const image = img ? `<img src="${img}" height="30%" width="30%"></img>` : ''  
  8.     const defaultStyle = "position:absolute;top:0;left:0;z-index:9999;background:#fff;display:flex;justify-content: center;align-items: center;"  
  9.     const empty = Vue.extend({  
  10.     template: `<div style="height:${offsetHeight}px;width:${offsetWidth}px;${defaultStyle}">  
  11.       <div style="text-align:center">  
  12.         <div>${image}</div>  
  13.         <div>${content || '暂无数据'}</div>  
  14.       </div>  
  15.     </div> 
  16.     })  
  17.     const component = new empty().$mount().$el  
  18.     if (visible) {  
  19.       el.appendChild(component)  
  20.     } else {  
  21.       el.removeChild(el.lastChild)  
  22.     }  
  23.   },  

参数 Attributes:

参数 说明 默认值 类型 可选
emptyValue 包含文字内容 content、图片 img、是否显示 visible,仅 visible 必传 / Object 必须

然后你可以在模板中任何元素上使用新的 v-empty property,如下传入对象 emptyValue: 

  1. <div style="height:500px;width:500px" v-empty="emptyValue"> 原本内容 

需要传入一个参数对象,例如显示文字为:暂无列表,图片路径为 ../../assets/images/blue_big.png,控制标示 visible: 

  1. emptyValue = {  
  2.   content: '暂无列表',  
  3.   img: require('../../assets/images/blue_big.png'),  
  4.   visible: true, 
  5. }, 

徽标指令 v-badge

使用该指令在元素右上角显示徽标。

支持配置徽标的背景颜色、徽标形状;支持传入徽标上显示的数字。

指令的代码如下: 

  1. import Vue from 'vue'  
  2. const SUCCESS = '#72c140'  
  3. const ERROR = '#ed5b56'  
  4. const WARNING = '#f0af41'  
  5. const INFO = '#4091f7'  
  6. const HEIGHT = 20  
  7. let flag = false  
  8. export default {  
  9.   update (el, binding, vnode) {  
  10.     const { modifiers, value } = binding  
  11.     const modifiersKey = Object.keys(modifiers) 
  12.      let isDot = modifiersKey.includes('dot')  
  13.     let backgroundColor = ''  
  14.     if (modifiersKey.includes('success')) {  
  15.       backgroundColor = SUCCESS  
  16.     } else if (modifiersKey.includes('warning')) {  
  17.       backgroundColor = WARNING  
  18.     } else if (modifiersKey.includes('info')) {  
  19.       backgroundColor = INFO  
  20.     } else {  
  21.       backgroundColor = ERROR  
  22.     }  
  23.     const targetTemplate = isDot   
  24.         ? `<div style="position:absolute;top:-5px;right:-5px;height:10px;width:10px;border-radius:50%;background:${backgroundColor}"></div>`  
  25.          : `<div style="background:${backgroundColor};position:absolute;top:-${HEIGHT / 2}px;right:-${HEIGHT / 2}px;height:${HEIGHT}px;min-width:${HEIGHT}px;border-radius:${HEIGHT / 2}px;text-align:center;line-height:${HEIGHT}px;color:#fff;padding:0 5px;">${value}</div>
  26.         elel.style.position = el.style.position || 'relative'  
  27.     const badge = Vue.extend({  
  28.       template: targetTemplate  
  29.     })  
  30.     const component = new badge().$mount().$el  
  31.     if (flag) {  
  32.       el.removeChild(el.lastChild)  
  33.     }  
  34.     el.appendChild(component)  
  35.     flag = true  
  36.   }  

参数 Attributes:

参数 说明 默认值 类型 可选
normal、dot 徽标形状normal为正常徽标;dot 仅为一个点 normal String 可选
success、error、info、warning 徽标颜色 error String 可选
number 徽标上显示的数字 / Number 可选

然后你可以在模板中任何元素上使用新的 v-badge property,如下: 

  1. <div v-badge.dot.info="badgeCount" style="height:50px;width:50px;background:#999"> </div> 

拖拽指令 v-drag

使用该指令可以对元素进行拖拽。

指令的代码如下: 

  1. export default {  
  2.   let _el = el  
  3.   document.onselectstart = function() {  
  4.     return false  //禁止选择网页上的文字  
  5.   }    
  6.   _el.onmousedown = e => {  
  7.     let disX = e.clientX - _el.offsetLeft //鼠标按下,计算当前元素距离可视区的距离  
  8.     let disY = e.clientY - _el.offsetTop  
  9.     document.onmousemove = function(e){       
  10.       let l = e.clientX - disX  
  11.       let t = e.clientY - disY;  
  12.       _el.style.left = l + "px"  
  13.       _el.style.top = t + "px"  
  14.     }  
  15.     document.onmouseup = e => {  
  16.       documentdocument.onmousemove = document.onmouseup = null  
  17.     }  
  18.     return false  
  19.   }  

然后你可以在模板中任何元素上使用新的 v-drag property,如下:

  1. <div v-drag> 支持拖拽的元素 </div> 

响应缩放指令 v-resize

使用该指令可以响应元素宽高改变时执行的方法。

指令的代码如下: 

  1. export default {  
  2.   bind(el, binding) {  
  3.     let width = ''height = ''
  4.     function isReize() {  
  5.       const style = document.defaultView.getComputedStyle(el);  
  6.       if (width !== style.width || height !== style.height) {  
  7.         binding.value();  // 执行传入的方法  
  8.       }  
  9.       width = style.width;  
  10.       height = style.height;  
  11.      }  
  12.      el.__timer__ = setInterval(isReize, 300); // 周期性监听元素是否改变  
  13.   },  
  14.   unbind(el) {  
  15.     clearInterval(el.__timer__);  
  16.   }  

参数 Attributes:

参数 说明 默认值 类型 可选
resize() 传入元素改变 size 后执行的方法 / Function 必选

然后你可以在模板中任何元素上使用新的 v-resize property,如下: 

  1. // 传入 resize() 方法  
  2. <div v-resize="resize"></div> 

字符串整形指令 v-format

使用该指令可以修改字符串,如使用 v-format.toFixed 保留两位小数、 v-format.price 将内容变成金额(每三位逗号分隔),可以同时使用,如 v-format.toFixed.price。

例如将数字 243112.331 变成 243112.33,或 243,112.33。

指令的代码如下: 

  1. export default {  
  2.   update (el, binding, vnode) {  
  3.     const { value, modifiers } = binding  
  4.     if (!value) return  
  5.     let formatValue = value  
  6.     if (modifiers.toFixed) {  
  7.       formatValue = value.toFixed(2) 
  8.      }  
  9.     console.log(formatValue)  
  10.     if (modifiers.price) {  
  11.       formatValue = formatNumber(formatValue)  
  12.     }  
  13.     el.innerText = formatValue  
  14.   },  
  15.   
  16. function formatNumber (num) {  
  17.   num += '';  
  18.   let strs = num.split('.');  
  19.   let x1 = strs[0];  
  20.   let x2 = strs.length > 1 ? '.' + strs[1] : '';  
  21.   var rgx = /(\d+)(\d{3})/;  
  22.   while (rgx.test(x1)) {  
  23.     x1x1 = x1.replace(rgx, '$1' + ',' + '$2');  
  24.   } 
  25.    return x1 + x2  

参数 Attributes:

参数 说明 默认值 类型 可选
toFixed 保留两位小数 / String 可选
price 整形成金额(三位逗号分隔) / String 可选

然后你可以在模板中任何元素上使用新的 v-format property,如下: 

  1. <div v-format.toFixed.price="123333"> 123 </div> 

如何使用这些指令?

为了便于管理指令,我们将每个指令都存在于单独的 js 文件中。在项目的 src 下建一个 directives 目录,目录下新建 index.js 文件用于引入并注册指令。 

  1. ├── src  
  2. |  ├── directive  
  3. |  |  ├── index.js  
  4. |  |  ├── backtop.js  
  5. |  |  ├── badge.js  
  6. |  |  ├── copy.js  
  7. |  |  ├── ellipsis.js  
  8. |  |  ├── empty.js  
  9. |  |  ├── expandClick.js  
  10. |  |  ├── screenfull.js  
  11. |  |  └── tooltips.js  
  12. |  ├── main.js 

举个例子:

directives 目录下新建 ellipsis.js 文件: 

  1. export default function (el, binding) {  
  2.     el.style.width = binding.arg || 100 + 'px' 
  3.     el.style.whiteSpace = 'nowrap'  
  4.     el.style.overflow = 'hidden' 
  5.     el.style.textOverflow = 'ellipsis' 

directives 的 index.js 文件中引入 ellipsis 指令并注册: 

  1. import Vue from 'vue'  
  2. import ellipsis from './ellipsis' // 引入指令  
  3. // import other directives  
  4. const directives = {  
  5.   ellipsis  
  6.   // other directives  
  7.  
  8. Object.keys(directives).forEach(name => Vue.directive(name, directives[name])) 

最后在 mian.js 中引入 index.js 文件:

  1. import '@/directives/index' 

这样就可以正常使用这些指令了: 

  1. <div v-指令名称 /> 

总结

我们常常在引入全局功能时,主要都是写于 js 文件、组件中。不同于他们在使用时每次需要引用或注册,在使用上指令更加简洁。

除了将功能封装成组件,还可以多多考虑将一些简洁实用的功能放到 deirect 中。例如:常用的 css 样式、js 的一些操作、utils 中的一些工具方法、甚至是一个完整的组件都可以放进去(不过需要考虑一下性能和复杂度)。

本文所写的所有指令源码收录于:

https://github.com/Gesj-yean/vue-demo-collection 

 

责任编辑:庞桂玉 来源: 前端大全
相关推荐

2022-02-22 13:14:30

Vue自定义指令注册

2020-12-28 10:10:04

Vue自定义指令前端

2023-06-28 08:05:46

场景vue3自定义

2021-11-30 08:19:43

Vue3 插件Vue应用

2023-07-21 19:16:59

OpenAIChatGPT

2022-07-26 01:06:18

Vue3自定义指令

2023-10-30 08:35:50

水波纹效果vue

2021-02-18 08:19:21

Vue自定义Vue 3.0

2015-02-12 15:33:43

微信SDK

2023-08-11 09:16:14

2015-02-12 15:38:26

微信SDK

2020-12-16 10:11:50

开发技能工具

2021-11-30 11:17:23

自定义配置插件

2016-11-16 21:55:55

源码分析自定义view androi

2016-12-26 15:25:59

Android自定义View

2011-06-23 10:49:13

Qt 自定义信号

2023-09-27 22:10:47

Vue.jsJavaScript

2022-04-08 08:11:44

自定义钩子Vuejs

2021-05-08 07:51:07

Vue框架前端

2023-11-12 22:08:01

Budgie桌面
点赞
收藏

51CTO技术栈公众号