位置: IT常识 - 正文

Vue自定义指令(含常用8种指令封装)(vue自定义指令生命周期)

编辑:rootadmin
Vue自定义指令(含常用8种指令封装) 基本概念

推荐整理分享Vue自定义指令(含常用8种指令封装)(vue自定义指令生命周期),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:vue自定义指令两种方式,vue自定义指令控制按钮权限,vue自定义指令生命周期,vue自定义指令生命周期,vue自定义指令两种方式,vue自定义指令,vue自定义指令钩子函数,vue自定义指令构造函数,内容如对您有帮助,希望把文章链接给更多的朋友!

一、自定义指令是用来操作dom的,尽管vue推崇数据驱动试图的理念,但并非所又情况都适合数据驱动。自定义指令就是一种有效的补充和扩展,不仅仅可用于定义任何dom操作,并且是可以复用的‘

比如谷歌图片的加载做的非常优雅,在图片未完成加载前,用随机的背景色占位背景图片加载晚成后才直接渲染出来。用自定义只能怪可以非常方便的实现这个功能。

Vue 自定义指令有全局注册和局部注册两种方式。先来看看注册全局指令的方式,通过 Vue.directive( id, [definition] ) 方式注册全局指令。然后在入口文件中进行 Vue.use() 调用。

全局:

比如一个input自动聚焦的例子:在main.js中写入以下内容

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

局部:

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

使用的时候要在自定义的指令前面加上v-,然后你可以在模板中任何元素上使用新的 v-focus

<input v-focus></input> 钩子函数

指令定义函数提供了几个钩子函数(可选,执行顺序排序)。

bind:只调用一次,指令第一次绑定到元素时调用,可以用这个钩子函数定义一个在绑定时执行一次的初始化动作。

inserted:被绑定的元素插入父节点时调用(父节点存在即可调用,不必存在于document中)。

update:被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新。

componentUpdated:被绑定元素所在模板完成一次更新周期时调用。

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

钩子函数的参数主要有以下几项

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

binding:一个对象,包含以下属性

name:指令名,不包括v-前缀。

value:指令的绑定值,例如v-my-directive=“1+1”,value的值是2。

oldValue:指令绑定的前一个值,仅在update和componentUpdated钩子中可用,无论值是否改变都可用。

expression:绑定值的表达式或变量名,例如v-my-directive=“1+1”,expression的值是"1+1"。

arg:传给指令的参数,例如v-my-directive:foo,arg的值是"foo"。

modifiers:一个包含修饰符的对象,例如v-my-directive.foo.bar,修饰符对象modifiers的值是{foo:true,bar:true}。

vnode:Vue编译生成的虚拟节点。

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

除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。如以下例子:

<div v-demo="{ color: 'white', text: 'hello!' }"></div><script>Vue.directive('demo', function (el, binding) {console.log(binding.value.color) // "white"console.log(binding.value.text) // "hello!"})</script>

一些自定义指令的应用场景:

防抖、图片懒加载、一键 Copy的功能、拖拽、页面水印、权限校验、输入框自动聚焦、相对时间转换、下拉菜单

批量注册指令

新建 directives/index.js 文件

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

在 main.js 引入并调用

import Vue from 'vue'import Directives from './JS/directives'Vue.use(Directives)下面分享几个实用的 Vue 自定义指令

复制粘贴指令 v-copy

长按指令 v-longpress

输入框防抖指令 v-debounce

禁止表情及特殊字符 v-emoji

图片懒加载 v-LazyLoad

权限校验指令 v-premission

实现页面水印 v-waterMarker

拖拽指令 v-draggable

1、v-copy

需求:

实现一键复制文本内容,用于鼠标右键粘贴。

思路:

动态创建 textarea 标签,并设置 readOnly 属性及移出可视区域

将要复制的值赋给 textarea 标签的 value 属性,并插入到 body

选中值 textarea 并复制

将 body 中插入的 textarea 移除

在第一次调用时绑定事件,在解绑时移除事件

const copy = {bind(el, { value }) {el.$value = valueel.handler = () => {if (!el.$value) {// 值为空的时候,给出提示。可根据项目UI仔细设计console.log('无复制内容')return}// 动态创建 textarea 标签const textarea = document.createElement('textarea')// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域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('复制成功') // 可根据项目UI仔细设计}document.body.removeChild(textarea)}// 绑定点击事件,就是所谓的一键 copy 啦el.addEventListener('click', el.handler)},// 当传进来的值更新的时候触发componentUpdated(el, { value }) {el.$value = value},// 指令与元素解绑的时候,移除事件绑定unbind(el) {el.removeEventListener('click', el.handler)},}export default copy

使用:

给 Dom 加上 v-copy 及复制的文本即可

<template><button v-copy="copyText">复制</button></template><script>export default {data() {return {copyText: 'a copy directives',}},}</script>2、v-longpress

需求:

实现长按,用户需要按下并按住按钮几秒钟,触发相应的事件

思路:

Vue自定义指令(含常用8种指令封装)(vue自定义指令生命周期)

创建一个计时器, 2 秒后执行函数

当用户按下按钮时触发 mousedown 事件,启动计时器;用户松开按钮时调用 mouseout 事件。

如果 mouseup 事件 2 秒内被触发,就清除计时器,当作一个普通的点击事件

如果计时器没有在 2 秒内清除,则判定为一次长按,可以执行关联的函数。

在移动端要考虑 touchstart,touchend 事件

const longpress = {bind: function (el, binding, vNode) {if (typeof binding.value !== 'function') {throw 'callback must be a function'}// 定义变量let pressTimer = null// 创建计时器( 2秒后执行函数 )let start = (e) => {if (e.type === 'click' && e.button !== 0) {return}if (pressTimer === null) {pressTimer = setTimeout(() => {handler()}, 2000)}}// 取消计时器let cancel = (e) => {if (pressTimer !== null) {clearTimeout(pressTimer)pressTimer = null}}// 运行函数const handler = (e) => {binding.value(e)}// 添加事件监听器el.addEventListener('mousedown', start)el.addEventListener('touchstart', start)// 取消计时器el.addEventListener('click', cancel)el.addEventListener('mouseout', cancel)el.addEventListener('touchend', cancel)el.addEventListener('touchcancel', cancel)},// 当传进来的值更新的时候触发componentUpdated(el, { value }) {el.$value = value},// 指令与元素解绑的时候,移除事件绑定unbind(el) {el.removeEventListener('click', el.handler)},}export default longpress

使用:

给 Dom 加上 v-longpress 及回调函数即可

<template><button v-longpress="longpress">长按</button></template><script>export default {methods: {longpress () {alert('长按指令生效')}}}</script>3、v-debounce

背景:

在开发中,有些提交保存按钮有时候会在短时间内被点击多次,这样就会多次重复请求后端接口,造成数据的混乱,比如新增表单的提交按钮,多次点击就会新增多条重复的数据。

需求:

防止按钮在短时间内被多次点击,使用防抖函数限制规定时间内只能点击一次。

思路:

定义一个延迟执行的方法,如果在延迟时间内再调用该方法,则重新计算执行时间。

将时间绑定在 click 方法上。

const debounce = {inserted: function (el, binding) {let timerel.addEventListener('keyup', () => {if (timer) {clearTimeout(timer)}timer = setTimeout(() => {binding.value()}, 1000)})},}export default debounce

使用:

给 Dom 加上 v-debounce 及回调函数即可

<template><button v-debounce="debounceClick">防抖</button></template><script>export default {methods: {debounceClick () {console.log('只触发一次')}}}</script>4、v-emoji

背景:

开发中遇到的表单输入,往往会有对输入内容的限制,比如不能输入表情和特殊字符,只能输入数字或字母等。

我们常规方法是在每一个表单的 on-change 事件上做处理。

<template><input type="text" v-model="note" @change="vaidateEmoji" /></template><script> export default {methods: {vaidateEmoji() {var reg = /[^u4E00-u9FA5|d|a-zA-Z|rns,.?!,。?!…—&$=()-+/*{}[]]|s/gthis.note = this.note.replace(reg, '')},},} </script>

这样代码量比较大而且不好维护,所以我们需要自定义一个指令来解决这问题。

需求:

根据正则表达式,设计自定义处理表单输入规则的指令,下面以禁止输入表情和特殊字符为例。

let findEle = (parent, type) => {return parent.tagName.toLowerCase() === type ? parent : parent.querySelector(type)}const trigger = (el, type) => {const e = document.createEvent('htmlEvents')e.initEvent(type, true, true)el.dispatchEvent(e)}const emoji = {bind: function (el, binding, vnode) {// 正则规则可根据需求自定义var regRule = /[^u4E00-u9FA5|d|a-zA-Z|rns,.?!,。?!…—&$=()-+/*{}[]]|s/glet $inp = findEle(el, 'input')el.$inp = $inp$inp.handle = function () {let val = $inp.value$inp.value = val.replace(regRule, '')trigger($inp, 'input')}$inp.addEventListener('keyup', $inp.handle)},unbind: function (el) {el.$inp.removeEventListener('keyup', el.$inp.handle)},}export default emoji

使用:

将需要校验的输入框加上 v-emoji 即可

<template><input type="text" v-model="note" v-emoji /></template>5、v-LazyLoad

背景:在类电商类项目,往往存在大量的图片,如 banner 广告图,菜单导航图,美团等商家列表头图等。图片众多以及图片体积过大往往会影响页面加载速度,造成不良的用户体验,所以进行图片懒加载优化势在必行。

需求:实现一个图片懒加载指令,只加载浏览器可见区域的图片。

思路:

图片懒加载的原理主要是判断当前图片是否到了可视区域这一核心逻辑实现的

拿到所有的图片 Dom ,遍历每个图片判断当前图片是否到了可视区范围内

如果到了就设置图片的 src 属性,否则显示默认图片

图片懒加载有两种方式可以实现,一是绑定 srcoll 事件进行监听,二是使用 IntersectionObserver 判断图片是否到了可视区域,但是有浏览器兼容性问题。

下面封装一个懒加载指令兼容两种方法,判断浏览器是否支持 IntersectionObserver API,如果支持就使用 IntersectionObserver 实现懒加载,否则则使用 srcoll 事件监听 + 节流的方法实现。

const LazyLoad = {// install方法install(Vue, options) {const defaultSrc = options.defaultVue.directive('lazy', {bind(el, binding) {LazyLoad.init(el, binding.value, defaultSrc)},inserted(el) {if (IntersectionObserver) {LazyLoad.observe(el)} else {LazyLoad.listenerScroll(el)}},})},// 初始化init(el, val, def) {el.setAttribute('data-src', val)el.setAttribute('src', def)},// 利用IntersectionObserver监听elobserve(el) {var io = new IntersectionObserver((entries) => {const realSrc = el.dataset.srcif (entries[0].isIntersecting) {if (realSrc) {el.src = realSrcel.removeAttribute('data-src')}}})io.observe(el)},// 监听scroll事件listenerScroll(el) {const handler = LazyLoad.throttle(LazyLoad.load, 300)LazyLoad.load(el)window.addEventListener('scroll', () => {handler(el)})},// 加载真实图片load(el) {const windowHeight = document.documentElement.clientHeightconst elTop = el.getBoundingClientRect().topconst elBtm = el.getBoundingClientRect().bottomconst realSrc = el.dataset.srcif (elTop - windowHeight < 0 && elBtm > 0) {if (realSrc) {el.src = realSrcel.removeAttribute('data-src')}}},// 节流throttle(fn, delay) {let timerlet prevTimereturn function (...args) {const currTime = Date.now()const context = thisif (!prevTime) prevTime = currTimeclearTimeout(timer)if (currTime - prevTime > delay) {prevTime = currTimefn.apply(context, args)clearTimeout(timer)return}timer = setTimeout(function () {prevTime = Date.now()timer = nullfn.apply(context, args)}, delay)}},}export default LazyLoad

使用,将组件内 标签的 src 换成 v-LazyLoad

<img v-LazyLoad="xxx.jpg" />6、v-permission

背景:

在一些后台管理系统,我们可能需要根据用户角色进行一些操作权限的判断,很多时候我们都是粗暴地给一个元素添加 v-if / v-show 来进行显示隐藏,但如果判断条件繁琐且多个地方需要判断,这种方式的代码不仅不优雅而且冗余。针对这种情况,我们可以通过全局自定义指令来处理。

需求:自定义一个权限指令,对需要权限判断的 Dom 进行显示隐藏。

思路:

自定义一个权限数组

判断用户的权限是否在这个数组内,如果是则显示,否则则移除 Dom

function checkArray(key) {let arr = ['1', '2', '3', '4']let index = arr.indexOf(key)if (index > -1) {return true // 有权限} else {return false // 无权限}}const permission = {inserted: function (el, binding) {let permission = binding.value // 获取到 v-permission的值if (permission) {let hasPermission = checkArray(permission)if (!hasPermission) {// 没有权限 移除Dom元素el.parentNode && el.parentNode.removeChild(el)}}},}export default permission

使用:

给 v-permission 赋值判断即可

<!-- 显示 --><button v-permission="'1'">权限按钮1</button><!-- 不显示 --><button v-permission="'10'">权限按钮2</button></div>7、vue-waterMarker

需求:给整个页面添加背景水印

思路:

使用 canvas 特性生成 base64 格式的图片文件,设置其字体大小,颜色等。

将其设置为背景图片,从而实现页面或组件水印效果

function addWaterMarker(str, parentNode, font, textColor) {// 水印文字,父元素,字体,文字颜色var can = document.createElement('canvas')parentNode.appendChild(can)can.width = 200can.height = 150can.style.display = 'none'var cans = can.getContext('2d')cans.rotate((-20 * Math.PI) / 180)cans.font = font || '16px Microsoft JhengHei'cans.fillStyle = textColor || 'rgba(180, 180, 180, 0.3)'cans.textAlign = 'left'cans.textBaseline = 'Middle'cans.fillText(str, can.width / 10, can.height / 2)parentNode.style.backgroundImage = 'url(' + can.toDataURL('image/png') + ')'}const waterMarker = {bind: function (el, binding) {addWaterMarker(binding.value.text, el, binding.value.font, binding.value.textColor)},}export default waterMarker

使用,设置水印文案,颜色,字体大小即可

<template><div v-waterMarker="{text:'lzg版权所有',textColor:'rgba(180, 180, 180, 0.4)'}"></div></template>8、v-draggable

需求:

实现一个拖拽指令,可在页面可视区域任意拖拽元素。

思路:

设置需要拖拽的元素为相对定位,其父元素为绝对定位。

鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。

鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值

鼠标松开(onmouseup)时完成一次拖拽

const draggable = {inserted: function (el) {el.style.cursor = 'move'el.onmousedown = function (e) {let disx = e.pageX - el.offsetLeftlet disy = e.pageY - el.offsetTopdocument.onmousemove = function (e) {let x = e.pageX - disxlet y = e.pageY - disylet maxX = document.body.clientWidth - parseInt(window.getComputedStyle(el).width)let maxY = document.body.clientHeight - parseInt(window.getComputedStyle(el).height)if (x < 0) {x = 0} else if (x > maxX) {x = maxX}if (y < 0) {y = 0} else if (y > maxY) {y = maxY}el.style.left = x + 'px'el.style.top = y + 'px'}document.onmouseup = function () {document.onmousemove = document.onmouseup = null}}},}export default draggable

使用:

在 Dom 上加上 v-draggable 即可

<template><div class="el-dialog" v-draggable></div></template>
本文链接地址:https://www.jiuchutong.com/zhishi/298467.html 转载请保留说明!

上一篇:解读YOLO v7的代码(三)损失函数(yolo v5详解)

下一篇:机器学习中的预测评价指标MSE、RMSE、MAE、MAPE、SMAPE

  • ipadmini打电话可以吗(ipadmini打电话)(ipad mini2打电话)

    ipadmini打电话可以吗(ipadmini打电话)(ipad mini2打电话)

  • 荣耀X30Max屏幕材质(荣耀x30i屏幕尺寸)

    荣耀X30Max屏幕材质(荣耀x30i屏幕尺寸)

  • iwatch怎么锁屏(iwatch怎么锁屏iphone)

    iwatch怎么锁屏(iwatch怎么锁屏iphone)

  • 1050ti和1650差多少(1050ti和1650差距多大)

    1050ti和1650差多少(1050ti和1650差距多大)

  • 小米10青春版充电器是多少w(小米10青春版充电充不进去)

    小米10青春版充电器是多少w(小米10青春版充电充不进去)

  • 远程连接怎么连不上(远程连接怎么连接微软账户)

    远程连接怎么连不上(远程连接怎么连接微软账户)

  • 苹果11pro max多少G(苹果11pro max多少厘米)

    苹果11pro max多少G(苹果11pro max多少厘米)

  • 拉黑了短信服务能发送吗(拉黑已用短信服务发送)

    拉黑了短信服务能发送吗(拉黑已用短信服务发送)

  • Word全文行距怎么设置(word全文行距怎么设置)

    Word全文行距怎么设置(word全文行距怎么设置)

  • 8g运行内存实际有多少(8g运行内存实际占用多少)

    8g运行内存实际有多少(8g运行内存实际占用多少)

  • 抖音不喜欢的怎么屏蔽(抖音不喜欢的怎么一直刷到)

    抖音不喜欢的怎么屏蔽(抖音不喜欢的怎么一直刷到)

  • 为什么苹果电脑只有一个c盘(为什么苹果电脑充不上电)

    为什么苹果电脑只有一个c盘(为什么苹果电脑充不上电)

  • 钉钉来电话为什么不响?(钉钉来电话为什么不响铃)

    钉钉来电话为什么不响?(钉钉来电话为什么不响铃)

  • 华为荣耀4c下载不了微信(华为荣耀4c怎么下载不了微信)

    华为荣耀4c下载不了微信(华为荣耀4c怎么下载不了微信)

  • vivox23魅影紫是幻彩版吗(vivox23幻影紫)

    vivox23魅影紫是幻彩版吗(vivox23幻影紫)

  • 手机爱奇艺设置在哪里(手机爱奇艺设置页面打开)

    手机爱奇艺设置在哪里(手机爱奇艺设置页面打开)

  • 苹果手机有9和10吗(苹果9和10是什么型号)

    苹果手机有9和10吗(苹果9和10是什么型号)

  • 手机内置听筒没声音怎么办(手机内部听筒)

    手机内置听筒没声音怎么办(手机内部听筒)

  • 手机如何开启省电模式(华为手机我的服务怎么关闭)

    手机如何开启省电模式(华为手机我的服务怎么关闭)

  • 抖音送礼记录如何删除(抖音送礼记录如何删除掉)

    抖音送礼记录如何删除(抖音送礼记录如何删除掉)

  • 手机怎么拍出景深效果(手机拍景物的小技巧)

    手机怎么拍出景深效果(手机拍景物的小技巧)

  • iphone11怎么截图(iphone11怎么截图长屏幕)

    iphone11怎么截图(iphone11怎么截图长屏幕)

  • 淘宝助力值怎么涨(淘宝助力榜怎么快速加助力)

    淘宝助力值怎么涨(淘宝助力榜怎么快速加助力)

  • 快手直播封面怎么更新不了(快手直播封面怎么和直播同步)

    快手直播封面怎么更新不了(快手直播封面怎么和直播同步)

  • 如何查看别人抖音点赞(如何查看别人抖音购物)

    如何查看别人抖音点赞(如何查看别人抖音购物)

  • 【创作赢红包】ChatGPT引爆全网引发的AI算力思考

    【创作赢红包】ChatGPT引爆全网引发的AI算力思考

  • 个人所得税应纳税所得额减半征收
  • 契税的具体适用税率是多少
  • 长期应收款的计税基础 陈版
  • 预收账款借方和贷方表示什么
  • 坏账计提坏账准备
  • 应付款多付了怎么处理
  • 企业内部控制调查问卷
  • 小企业销售材料计入什么科目借方记什么
  • 买一赠一视同销售征收增值税吗
  • 应交税费应交增值税减免税款
  • 行政单位应缴财政收入预算会计分录
  • 转让土地使用权一般计税方法
  • 收取员工宿舍租金收入要交增值税吗
  • 帮别的公司做账的叫什么公司
  • 增值税免税收入进项税额转出
  • 没有增值税发票怎么出口
  • 分公司所得税怎么交
  • 税控盘抵扣增值税怎么做账
  • 公司股东会承担法律责任吗
  • 开票软件安装6位地区编号填什么
  • 企业公益救济性工作总结
  • 房屋融资租赁会计处理
  • linux命令执行成功后会返回什么
  • 广告公司员工
  • 报税系统维护费怎么入账
  • 苹果桌面小工具怎么设置
  • 劳务外包开什么样的发票
  • mysql 数据源
  • windows无法自动检测网络代理设置该怎么办
  • 如何看待税收的本质
  • 应扣未扣税款对纳税人的处理
  • win10电脑记事本在哪
  • win7系统安装包多大
  • 发票金额与银行付款金额少几分钱
  • 盈余公积弥补以前年度亏损所有者权益会增加
  • 约书亚树国家公园
  • 注意力机制详解
  • phpstudy删除
  • 微信小程序的视频怎么下载
  • thinkphp项目怎么部署
  • chrome插件开发语言
  • /f命令
  • 付临时工工资需要什么材料
  • 破解版微擎框架如何升级
  • 不征税收入的账务处理
  • php网站首页的源码在哪里
  • 将织梦dedecms转换到wordpress
  • 生产型企业可以买进就卖出吗
  • mysql建表的完整步骤
  • mongodb集群开启auth认证
  • mongodb bi
  • 预算收入的核算
  • 代开运输发票会不会造成重复征税
  • 增值税发票认证勾选平台
  • 单位全额承担社保能税前扣除吗
  • 账龄划分中有借有贷怎么分析
  • 银行代发工资流程
  • 怎么做掉公司账面库存100万
  • 我国现行资源税主要采用什么税率
  • 低值易耗品怎么摊
  • 账务处理程序的种类及各自的适用范围
  • 为什么贷款要收手续费
  • 自助回单打印和网银回单
  • 企业清算主要清算哪些项目?
  • xp系统电脑设置在哪
  • ubuntu安装超详细教程
  • centos的优缺点
  • win10快速切换到桌面快捷键
  • win10系统光驱
  • .fs是什么格式
  • mpcmdrun.exe是什么进程
  • jquery 插件写法
  • activity传递数据
  • listview安卓
  • cocos2dx 2.2.2
  • python中读取excel
  • 浅谈jquery中next与siblings的区别
  • 批处理文件
  • 安卓监听文件读写
  • acfunp
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设