位置: IT常识 - 正文

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

发布时间:2024-01-17
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

  • 本期收入和本期减除费用
  • 卖出看跌期权的损益图
  • 劳务公司临时工工资需要申报吗
  • 盘盈的固定资产怎么做账务处理
  • 收到的普通发票需要认证吗
  • 长期借款借方登记归还的本金和利息
  • 提取备用金如何在退回公司
  • 别人帮开票钱要打给别人账户吗
  • 以银行存款退还投资者股金
  • 企业完税凭证入账后怎么办理退税?
  • 非住宅过户税费标准最新
  • 营改增对固定资产的影响
  • 什么时候计提减值
  • 电子发票怎么作废操作流程视频
  • 建筑工程企业人力资源管理
  • 劳务报酬和个人工资的区别
  • 同比增长率为负数怎么算
  • 企业所得税营业收入
  • 业务招待费不超过销售收入的5‰
  • 4s店以车换车怎么算的
  • 利息支出怎么结转
  • 电脑怎么纯净启动
  • 交城建税的滞纳金是多少
  • 苹果六微信
  • 土地增值税清算管理规程
  • 借支单还款后借支单要还么
  • thinkphp实现163、QQ邮箱收发邮件的方法
  • 若依框架讲解
  • 企业购买预付卡送人分录
  • 学生兼职收入按什么交个税
  • 利用php判断是否为素数
  • 开具增值税专用发票的要求
  • 哪些税改将影响家庭个人财富
  • 进销项抵扣规则
  • php504错误
  • 承兑贴现会计分录怎么做
  • 前端cgi
  • 企业购买的土地出买后无法收回资金属国有资产流失吗?
  • 充电桩折旧年限是多少年
  • 残保金工资总额是计提还是实发
  • 无形资产商标权摊销
  • 公司帮非公司员工缴税
  • 购买黄金会计分录怎么写
  • 凭样品销售
  • python np数组
  • access中宏是按什么调用的
  • 外贸企业 生产企业
  • 营业执照变更需要本人到场吗
  • 非广告性质的赞助支出调增还是调减
  • sql怎么搜索表
  • 开农贸市场拿补贴找哪个部门
  • 建筑发票开具与土增税扣有什么关系?
  • 研发费用归集的主要范围有哪些
  • 已认证的红字发票怎么开
  • 行政单位无偿划拨资产账务处理
  • 其他货币的账面价值包括
  • 餐饮业固定资产怎么摊销
  • 个体户办营业执照网上怎么申请
  • 公司支付劳务工资怎么做账
  • 工程项目分包需要招标吗
  • mysql varchar2
  • 电脑bios怎么关
  • 设置centos时区修改时间的方法
  • win7系统关机很慢什么原因
  • win8应用商店废了
  • enterasys
  • xp系统强制删除文件
  • Red Hat Enterprise Linux AS release 4 apache+MYsql+PHP的安装和优化
  • centos配ip的方法
  • win10正版和盗版区别大吗
  • win8开机自启动在哪里设置
  • windows where命令
  • linux系统安装驱动
  • jquery 定位
  • 用bat调用exe并输入参数
  • flex开发工具
  • jquery设置cookie值
  • 企业投资项目备案表模板
  • 低丰度油气田开采的原油
  • 企业税收筹划的基本目标
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号