有的情况下,需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。

自定义指令使用情景:

1.按钮级别权限的控制。

2.按钮的波纹动态效果。

3.一键copy的功能。

4.输入框自动聚焦。

5.下拉菜单,点击下拉菜单以外的地方时隐藏下拉菜单。

6.时间转换,比如朋友圈发布动态后的相对时间,比如刚刚、两分钟前等等。

7.输入框的最小值和最大值限制。

一:自定义指令有全局注册指令和局部注册指令两种方式:

全局注册指令:

Vue.directive('focus',{bind:function(){},inserted:function(){},update:function(){},componentUpdated:function(){},unbind:function(){}});

局部注册指令:在.vue文件中使用directives属性:

directives:{focus:{bind:function(){},inserted:function(){},update:function(){},componentUpdated:function(){},unbind:function(){}}}

注册指令成功后,直接在dom元素上使用v-focus。

<input v-focus/>

二:注册指令的使用。

下面以局部注册指令来举例子。

自定义指令有4个钩子函数,v-check-num="{key:'myNum',maxval:1000,minval:100}"

钩子函数需要用到的参数解析:

1.el:指令所绑定的元素。

2.binding:绑定对象。属性包含

name(指令名),不包括v-,此例子中为check-num。

value(计算后的指令所绑定的值),此例子中为{key:'myNum',maxval:1000,minval:100}。

oldValue(指令所绑定的前一个值,仅在update和componentUpdated中可用)。

express(绑定的值的字符串形式),此例子中为'{key:'myNum',maxval:1000,minval:100}'。

arg(传递给指令的参数),v-check-num:a中的arg为a。

modifiers(修饰符对象),v-my-directive.foo.bar中的modifiers为{foo:true,bar:true}。

3.vnode:编译生成的虚拟节点。属性有context为虚拟节点的上下文。

注意:el可读可写,其他参数只读。如果需要在钩子函数之间共享数据,可通过dataset来实现。

4.oldVnode:上一个虚拟节点。

directiveCom.vue:

<template><div><p>checkNum指令</p><label>数量:</label><input :value="myNum" v-check-num="{key:'myNum',maxval:1000,minval:100}" v-if="show" /><button @click="toggle">切换一下show</button></div>
</template><script>
export default {name: "directiveCom",data: () => {return {show: true,myNum: "" //奖品总数};},methods: {toggle() {this.show = !this.show;}},directives: {checkNum: {//只调用一次,第一次绑定指令到元素上时调用,可在此生命周期内做一些初始化的操作bind: function() {console.log("bind");},//被绑定元素插入父节点时调用inserted: function() {console.log("inserted");},//被绑定元素所在的模板被更新时即可调用update: function() {console.log("update");},//被绑定元素所在的模板完成一次更新周期时调用componentUpdated: function() {console.log("componentUpdated");},//指令与元素解绑的时候调用unbind: function() {console.log("unbind");}}}
};
</script> <style>
</style>

在App.vue里面引用上面的指令组件:

<template><div id="app"><directive-com/></div>
</template><script>import directiveCom from './components/directiveCom'
export default {name: "App",components:{directiveCom}
};
</script><style>
#app {font-family: Avenir, Helvetica, Arial, sans-serif;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;text-align: center;color: #2c3e50;margin-top: 60px;
}
</style>

效果图:

页面渲染时,触发了bind和inserted函数。

点击按钮,效果图:

此时,改变了directiveCom组件里的dom元素的css样式,触发了update和componentUpdated函数。

将directiveCom中的v-show改为v-if:

<template><div><p>checkNum指令</p><label>数量:</label><input :value="myNum" v-check-num="{key:'myNum',maxval:1000,minval:100}" v-if="show" /><button @click="toggle">切换一下show</button></div>
</template><script>
export default {name: "directiveCom",data: () => {return {show: true,myNum: "" //奖品总数};},methods: {toggle() {this.show = !this.show;}},directives: {checkNum: {//只调用一次,第一次绑定指令到元素上时调用,可在此生命周期内做一些初始化的操作bind: function() {console.log("bind");},//被绑定元素插入父节点时调用inserted: function() {console.log("inserted");},//被绑定元素所在的模板被更新时即可调用update: function() {console.log("update");},//被绑定元素所在的模板完成一次更新周期时调用componentUpdated: function() {console.log("componentUpdated");},//指令与元素解绑的时候调用unbind: function() {console.log("upbind");}}}
};
</script> <style>
</style>

点击按钮,效果如下:

v-if是dom组件的的销毁和创建,指令与元素解绑,此时触发了unbind函数。

三:进阶:使用自定义指令来设置输入框的最小值和最大值规则。规则:如果输入的值<最小值,那么默认为最小值;如果输入的值>最大值,那么默认为最大值;如果输入的是非数字,则清空输入框,默认为空。

directiveCom组件:

<template>
<div><p>checkNum指令</p><label>数量:</label><input :value="myNum" v-check-num="{key:'myNum',maxval:1000,minval:100}" v-if="show" /><button @click="toggle">切换一下show</button></div>
</template><script>
export default {name: "directiveCom",data: () => {return {show: true,myNum: "" //奖品总数};},methods: {toggle() {this.show = !this.show;}},directives: {checkNum: {//只调用一次,第一次绑定指令到元素上时调用,可在此生命周期内做一些初始化的操作bind: function(el, binding, vnode) {el.handler = function() {if (!Number(el.value)) {el.value = "";return false;}const value = Number(el.value);if (binding.value.maxval && value > binding.value.maxval) {el.value = binding.value.maxval;}if (binding.value.minval && value < binding.value.minval) {el.value = binding.value.minval;}//将el.val的值赋值给myNumvnode["context"][binding.value.key] = el.value;};el.addEventListener("change", el.handler);console.log("bind");},//被绑定元素插入父节点时调用inserted: function() {console.log("inserted");},//被绑定元素所在的模板被更新时即可调用update: function() {console.log("update");},//被绑定元素所在的模板完成一次更新周期时调用componentUpdated: function() {console.log("componentUpdated");},//指令与元素解绑的时候调用unbind: function() {console.log("unbind");}}}
};
</script> <style>
</style>

四:用自定义指令来实现几种需求场景:

git链接:https://github.com/xiaoli0510/vue-directive

directive.vue:

<template><div><div><p>1.validBtn指令:按钮级别权限的控制</p><button v-valid-btn="'viewBtn'">查看按钮</button><br /><!-- 通过ajax获取的用户按钮权限数组里面没有editBtn,所以编辑按钮被移除 --><button v-valid-btn="'editBtn'">编辑按钮</button></div><div><p>2.waves指令:按钮的波纹动态效果</p><button v-waves>点击有波纹效果的按钮</button></div><div><p>3.copy指令:一键copy的功能</p><button v-copy="'copyText'">点击一键复制</button></div><div><p>4.focus指令:输入框自动聚焦</p><input type="text" v-focus /></div><div><p>5.clickoutside指令:下拉菜单,点击下拉菜单以外的地方时隐藏下拉菜单</p><div class="main" v-clickoutside="handleClose"><button @click="showDrop =!showDrop">点击显示下拉菜单</button><div class="dropdown" v-show="showDrop"><p>我是下拉框的内容,点击外部区域可以关闭</p></div></div></div><div><p>7.time指令:时间转换,比如朋友圈发布动态后的相对时间,比如刚刚、两分钟前等等</p><div v-time="timeNow"></div></div><div><p>8.checkNum指令:输入框的最小值和最大值限制</p><label>数量:</label><input :value="myNum" v-check-num="{key:'myNum',maxval:1000,minval:100}" /></div></div>
</template><script>
export default {name: "directiveCom",data: () => {return {showDrop: false,myNum: "",timeNow: new Date().getTime()};},methods: {handleClose() {this.showDrop = false;}},directives: {//输入框的最小值和最大值限制checkNum: {//指令第一次绑定在元素上时调用,只调用一次bind: function(el, binding, vnode) {el._handler_ = function() {let value = el.value;if (!Number(value)) {el.value = "";return false;}value = Number(value);if (binding.value.maxval && value > binding.value.maxval) {el.value = binding.value.maxval;}if (binding.value.minval && value < binding.value.minval) {el.value = binding.value.minval;}vnode["context"][binding.value.key] = el.value;};el.addEventListener("change", el._handler_);},//被绑定的元素插入父节点时调用inserted: function() {},//被绑定的元素所在的模板发送更新时,比如style或者内容改变的时候调用update: function() {},//所在的模板完成一次更新后调用componentUpdated: function() {},//指令与元素解绑的时候调用unbind: function(el) {delete el._handler_;}},//按钮级别权限的控制validBtn: {inserted: function(el, binding) {el.handler = function(value) {//此处模拟的是ajax获取的用户权限按钮const btnArr = ["viewBtn"];for (var item of btnArr) {if (item === value) {return true;}}//如果用户没有此按钮权限,则移除此按钮el.parentNode.removeChild(el);return false;};el.handler(binding.value);},unbind: function(el) {delete el.handler;}},focus: {inserted: function(el) {el.focus();}},clickoutside: {bind: function(el, binding) {function documentHandler(e) {//如果点击的是当前的下拉框元素 则不做处理if (el.contains(e.target)) {return false;}//如果v-clickoutside后面有表达式 则执行后面的函数 此例子中执行handleClose函数if (binding.expression) {binding.value(e);}}el.__vueClickOutside__ = documentHandler;document.addEventListener("click", documentHandler);},unbind: function(el) {document.removeEventListener("click", el.__vueClickOutside__);delete el.__vueClickOutside__;}}}
};
</script> <style>
button {margin-bottom: 10px;
}
</style>

先上效果图:

checkNum、validBtn、focus、clickoutside是局部注册指令。

waves、copy、time是全局注册指定。

time.js:

var Time = {//获取当前时间戳getUnix: function () {var date = new Date();return date.getTime();},//获取今天0点0分0秒的时间戳getTodayUnix: function () {var date = new Date();date.setHours(0);date.setMinutes(0);date.setSeconds(0);date.setMilliseconds(0);return date.getTime();},//获取今年1月1日0点0分0秒的时间戳getYearUnix: function () {var date = new Date();date.setMonth(0);date.setDate(1);date.setHours(0);date.setMinutes(0);date.setSeconds(0);date.setMilliseconds(0);return date.getTime();},//获取标准年月日getLastDate: function (time) {var date = new Date(time);var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();return date.getFullYear() + '-' + month + '-' + day;},//转换时间getFormatTime: function (timestamp) {var now = this.getUnix(); //当前时间戳var today = this.getTodayUnix(); //今天0点时间戳//var year = this.getYearUnix(); //今年0点时间戳var timer = (now - timestamp) / 1000; //转换为妙级别时间戳var tip = '';if (timer <= 0) {tip = '刚刚';} else if (Math.floor(timer / 60) <= 0) {tip = '刚刚';} else if (timer < 3600) {tip = Math.floor(timer / 60) + '分钟前';} else if (timer >= 3600 && (timestamp - today >= 0)) {tip = Math.floor(timer / 3600) + '小时前';} else if (timer / 86400 <= 31) {tip = Math.ceil(timer / 86400) + '天前';} else {tip = this.getLastDate(timestamp);}return tip;}
};export default {bind: function (el, binding) {el.innerHTML = Time.getFormatTime(binding.value);el._timeout_ = setInterval(function () {el.innerHTML = Time.getFormatTime(binding.value);}, 1000);},unbind:function(el){clearInterval(el._timeout_);delete el._timeout_;}
}

copy.js:


let listenActionexport default {inserted(el, binding) {const params = binding.value || {}const stickyTop = params.stickyTop || 0const zIndex = params.zIndex || 1000const elStyle = el.styleelStyle.position = '-webkit-sticky'elStyle.position = 'sticky'// if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)// if (~elStyle.position.indexOf('sticky')) {//     elStyle.top = `${stickyTop}px`;//     elStyle.zIndex = zIndex;//     return// }const elHeight = el.getBoundingClientRect().heightconst elWidth = el.getBoundingClientRect().widthelStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`const parentElm = el.parentNode || document.documentElementconst placeholder = document.createElement('div')placeholder.style.display = 'none'placeholder.style.width = `${elWidth}px`placeholder.style.height = `${elHeight}px`parentElm.insertBefore(placeholder, el)let active = falseconst getScroll = (target, top) => {const prop = top ? 'pageYOffset' : 'pageXOffset'const method = top ? 'scrollTop' : 'scrollLeft'let ret = target[prop]if (typeof ret !== 'number') {ret = window.document.documentElement[method]}return ret}const sticky = () => {if (active) {return}if (!elStyle.height) {elStyle.height = `${el.offsetHeight}px`}elStyle.position = 'fixed'elStyle.width = `${elWidth}px`placeholder.style.display = 'inline-block'active = true}const reset = () => {if (!active) {return}elStyle.position = ''placeholder.style.display = 'none'active = false}const check = () => {const scrollTop = getScroll(window, true)const offsetTop = el.getBoundingClientRect().topif (offsetTop < stickyTop) {sticky()} else {if (scrollTop < elHeight + stickyTop) {reset()}}}listenAction = () => {check()}window.addEventListener('scroll', listenAction)},unbind() {window.removeEventListener('scroll', listenAction)}
}

waves.js:

import './waves.css'const context = '@@wavesContext'function handleClick(el, binding) {function handle(e) {const customOpts = Object.assign({}, binding.value)const opts = Object.assign({ele: el, // 波纹作用元素type: 'hit', // hit 点击位置扩散 center中心点扩展color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色},customOpts)const target = opts.eleif (target) {target.style.position = 'relative'target.style.overflow = 'hidden'const rect = target.getBoundingClientRect()let ripple = target.querySelector('.waves-ripple')if (!ripple) {ripple = document.createElement('span')ripple.className = 'waves-ripple'ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'target.appendChild(ripple)} else {ripple.className = 'waves-ripple'}switch (opts.type) {case 'center':ripple.style.top = rect.height / 2 - ripple.offsetHeight / 2 + 'px'ripple.style.left = rect.width / 2 - ripple.offsetWidth / 2 + 'px'breakdefault:ripple.style.top =(e.pageY - rect.top - ripple.offsetHeight / 2 - document.documentElement.scrollTop ||document.body.scrollTop) + 'px'ripple.style.left =(e.pageX - rect.left - ripple.offsetWidth / 2 - document.documentElement.scrollLeft ||document.body.scrollLeft) + 'px'}ripple.style.backgroundColor = opts.colorripple.className = 'waves-ripple z-active'return false}}if (!el[context]) {el[context] = {removeHandle: handle}} else {el[context].removeHandle = handle}return handle
}export default {bind(el, binding) {el.addEventListener('click', handleClick(el, binding), false)},update(el, binding) {el.removeEventListener('click', el[context].removeHandle, false)el.addEventListener('click', handleClick(el, binding), false)},unbind(el) {el.removeEventListener('click', el[context].removeHandle, false)el[context] = nulldelete el[context]}
}

waves.css:

.waves-ripple {position: absolute;border-radius: 100%;background-color: rgba(0, 0, 0, 0.15);background-clip: padding-box;pointer-events: none;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;-webkit-transform: scale(0);-ms-transform: scale(0);transform: scale(0);opacity: 1;
}.waves-ripple.z-active {opacity: 0;-webkit-transform: scale(2);-ms-transform: scale(2);transform: scale(2);-webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;transition: opacity 1.2s ease-out, transform 0.6s ease-out;transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
}

在main.js中引入js文件,再进行全局注册:

main.js:

import Vue from 'vue'
import App from './App.vue'Vue.config.productionTip = falseimport waves from './directive/waves/waves.js'
Vue.directive('waves', waves)import copy from './directive/copy.js'
Vue.directive('copy', copy)import time from './directive/time.js'
Vue.directive('time', time)new Vue({render: h => h(App),
}).$mount('#app')

vue directives自定义指令的使用相关推荐

  1. VUE:自定义指令(directives )选项的用法

    自定义指令分为全局指令和局部指令.全局指令可在任意vue组件内生效,局部指令仅在注册了指令的组件内生效. 全局指令和局部指令写法几乎相同.全局指令是在main.js内使用Vue.directive注册 ...

  2. Vue.js自定义指令的用法与实例

    市面上大多数关于Vue.js自定义指令的文章都在讲语法,很少讲实际的应用场景和用例,以致于即便明白了怎么写,也不知道怎么用.本文不讲语法,就讲自定义指令的用法. 自定义指令是用来操作DOM的.尽管Vu ...

  3. VUE使用自定义指令对普通 DOM 元素进行底层操作

    使用vue的自定义指令来整合某个重复使用的底层操作 举个聚焦输入框的例子,如下: 当页面加载时,该元素将获得焦点 (注意:autofocus 在移动版 Safari 上不工作).事实上,只要你在打开这 ...

  4. Vue的自定义指令以及Vue自定义指令的应用场景

    前言 一.Vue的自定义指令分为? vue中除了核心功能内置的指令外,也允许注册自定义指令.有的情况下,对普通DOM元素进行底层操作,这时候就会用到自定义指令. 自定义指令分为全局自定义指令和局部的自 ...

  5. VUE通过自定义指令,只允许输入大写英文以及数字

    在写之前需要知道几个功能对象的作用: 首先输入框输入的情况有键盘输入和粘贴输入的情况 用到的组件有: input.onblur-onblur: 事件会在对象失去焦点时发生.Onblur 经常用于Jav ...

  6. js:Vue.js自定义指令实现scroll下滑滚动翻页

    Vue.js自定义指令实现scroll下滑滚动翻页 核心代码 import Vue from 'vue'// v-scroll Vue.directive('scroll', {bind(el, bi ...

  7. vue 项目自定义指令实现防抖

    自定义v-debounce指令实现click事件防抖 该指令绑定的方法暂不支持传递参数,如需要传参数可以参考另一篇博客,防抖组件的实现 工具uitls文件夹创建debounce.js文件 代码如下 i ...

  8. Vue API(directives) 自定义指令

    前言:除了vue的内置指令以外,我们可以定义自定义指令.内置指令表相见:https://www.cnblogs.com/ilovexiaoming/p/6840383.html 我们定义一个最简单的 ...

  9. Vue.js 自定义指令

    简介 除了核心功能默认内置的指令 (v-model 和 v-show),Vue 也允许注册自定义指令.注意,在 Vue2.0 中,代码复用和抽象的主要形式是组件.然而,有的情况下,你仍然需要对普通 D ...

最新文章

  1. 时序约束,STA的QA
  2. 由Linux内核bug引起SSH登录缓慢问题的排查与解决
  3. 慈溪微生活图标_日常生活中的图标
  4. 北邮OJ 89. 统计时间间隔
  5. Charles调试Https iOS
  6. Myeclipse创建第一个web项目
  7. 加速你的Python
  8. 使用 Akka 实现 Master 与 Worker 之间的通信
  9. phpcmsV9上传文件类型的设置
  10. Spring Cloud 微服务实战系列-Eureka注册中心(一)
  11. maven(二) maven项目构建ssh工程(父工程与子模块的拆分与聚合)
  12. wps怎么把当前页面设置为横向_办公软件操作技巧011:如何将word文档的部分页面改为横向...
  13. GitHub Windows桌面版 中文汉化
  14. 高并发之——并发测试工具ab
  15. JAVA :一张纸厚0.5mm //0.0005m,折叠多少次,厚度会超过珠穆朗玛峰?(8848.43m)
  16. 伽利略定位系统的历史
  17. socket文件传输
  18. C#应用程序与MATLAB联合编程
  19. 【C++】Clang-Format:代码自动格式化(看这一篇就够了)
  20. C#-- 控制台操作

热门文章

  1. ubuntu 18.04 使用intel核显画面撕裂解决办法
  2. 深入浅出C++ ——初识C++
  3. 高鸿业微观经济学第8版笔记和课后答案
  4. windows git bash 设置多个php版本和composer版本
  5. C#程序设计之windows应用程序设计基础
  6. association weak 属性
  7. 信息系统面临的安全风险
  8. 时钟周期是干什么的?底层原理是什么?
  9. 什么是DDOS流量攻击,DDoS防护手段
  10. 屏幕录制编辑软件:Screenium 3 for Mac