最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 分享几个Vue 自定义指令(修改版)

    正文概述 掘金(_馒头)   2021-01-22   695

    来源 | github.com/Michael-lzg… 前天看到github上有人分享Vue自定义指令结合实际业务需求调整做出了优化版本,不喜勿喷。

    在 Vue,除了核心功能默认内置的指令 ( v-model 和 v-show ),Vue 也允许注册自定义指令。它的作用价值在于当开发人员在某些场景下需要对普通 DOM 元素进行操作。

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

    在 main.js 引入并调用

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

    批量注册指令,新建 directives/index.js 文件

    import copy from './copy'
    import longpress from './longpress'
    import debounce from './debounce'
    import spare from './spare'
    // 自定义指令
    const directives = {
      copy, // 复制粘贴指令
      longpress, // 长按指令
      debounce, // 输入框防抖指令
      spare // 图片加载失败显示备用图指令
    }
    export default {
      install(Vue) {
        Object.keys(directives).forEach(key => {
          Vue.directive(key, directives[key])
        })
      }
    } 
    

    指令定义函数提供了几个钩子函数(可选):

    • bind: 只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作。
    • inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
    • update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值。
    • componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
    • unbind: 只调用一次, 指令与元素解绑时调用。

    下面分享几个实用的 Vue 自定义指令

    • 复制粘贴指令 v-copy
    • 长按指令 v-longpress
    • 输入框防抖指令 v-debounce
    • 图片加载失败显示备用图指令 v-spare

    1、v-copy

    需求:

    实现一键复制文本内容,用于鼠标右键粘贴,部分场景可能需要有回调。

    思路:

    1. 动态创建 textarea 标签,并设置 readOnly 属性及移出可视区域
    2. 将要复制的值赋给 textarea 标签的 value 属性,并插入到 body
    3. 选中值 textarea 并复制
    4. 将 body 中插入的 textarea 移除
    5. 在第一次调用时绑定事件,在解绑时移除事件
    const copy = {
      bind(el, binding) {
        el.$value = binding.value
        el.handler = () => {
          // 判断值为空的时候,给出提示
          if (typeof el.$value !== 'object') {
            if (!el.$value) {
              // 可根据项目UI设计默认提示
              console.log('无复制内容')
              return
            }
          } else {
            if (!el.$value.text) {
              // 有回调则执行回调函数
              el.$value.callback(false)
              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 属性
          if (typeof el.$value !== 'object') {
            textarea.value = el.$value
          } else {
            textarea.value = el.$value.text
          }
          // 将 textarea 插入到 body 中
          document.body.appendChild(textarea)
          // 选中值并复制
          textarea.select()
          const result = document.execCommand('Copy')
          if (typeof el.$value !== 'object') {
            if (result) {
              // 复制成功可根据项目UI设计默认提示
              console.log('复制成功')
            } else {
              // 复制失败可根据项目UI设计默认提示
              console.log('复制失败')
            }
          } else {
            if (result) {
              // 复制成功有回调则执行回调函数
              el.$value.callback(true)
            } else {
              // 复制失败有回调则执行回调函数
              el.$value.callback(false)
            }
          }
          document.body.removeChild(textarea)
        }
        // 添加事件监听器
        el.addEventListener('click', el.handler)
      },
      // 当传进来的值更新的时候触发
      componentUpdated(el, binding) {
        el.$value = binding.value
      },
      // 指令与元素解绑的时候,移除事件绑定
      unbind(el) {
        el.removeEventListener('click', el.handler)
      }
    }
    export default copy
    // 传入String 无回调函数
    // <div v-copy="'复制内容'">复制按钮</div>
    // 传入Object 回调callback回调参数Boolean true成功 false 失败
    // <div v-copy="{ text: '复制内容', callback: copyCallback }">复制按钮</div>
    

    使用:

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

    <template>
      <div>
        <div v-copy="copyText">复制</div>
        <div v-copy="{ text: copyText, callback: copyCallback }">复制</div>
      </div>
    </template>
    <script>
    export default {
      data() {
        return {
          copyText: '复制内容'
        }
      },
      methods: {
        copyCallback(CallbackVal) {
          console.log(CallbackVal)
        }
      }
    }
    </script>
    
    

    2、v-longpress

    需求:

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

    思路:

    1. 创建一个计时器, 2 秒后执行函数
    2. 当用户按下按钮时触发 mousedown 事件,启动计时器;用户松开按钮时调用 mouseout 事件。
    3. 如果 mouseup 事件 2 秒内被触发,就清除计时器,当作一个普通的点击事件
    4. 如果计时器没有在 2 秒内清除,则判定为一次长按,可以执行关联的函数。
    5. 在移动端要考虑 touchstart,touchend 事件
    const longpress = {
      bind: function(el, binding) {
        el.$value = binding.value
        // 定义计时器容器
        let timer = null
        // 创建计时器 (2秒后执行函数)
        el.start = e => {
          if (typeof el.$value !== 'function') {
            throw 'callback must be a function'
          }
          if (e.type === 'click' && e.button !== 0) {
            return
          }
          if (timer === null) {
            timer = setTimeout(() => {
              // 运行函数
              el.$value()
            }, 2000)
          }
        }
        // 取消计时器
        el.cancel = () => {
          if (timer !== null) {
            clearTimeout(timer)
            timer = null
          }
        }
        // 添加事件监听器
        el.addEventListener('mousedown', el.start)
        el.addEventListener('touchstart', el.start)
        // 取消计时器
        el.addEventListener('click', el.cancel)
        el.addEventListener('mouseout', el.cancel)
        el.addEventListener('touchend', el.cancel)
        el.addEventListener('touchcancel', el.cancel)
      },
      // 当传进来的值更新的时候触发
      componentUpdated(el, binding) {
        el.$value = binding.value
      },
      // 指令与元素解绑的时候,移除事件绑定
      unbind(el) {
        el.removeEventListener('mousedown', el.start)
        el.removeEventListener('touchstart', el.start)
        el.removeEventListener('click', el.cancel)
        el.removeEventListener('mouseout', el.cancel)
        el.removeEventListener('touchend', el.cancel)
        el.removeEventListener('touchcancel', el.cancel)
      }
    }
    
    export default longpress
    
    // <div v-longpress="longpress">长按</div>
    

    使用:

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

    <template>
      <button v-longpress="longpress">长按</button>
    </template>
    <script>
    export default {
      methods: {
        longpress() {
          console.log('长按指令生效')
        }
      }
    }
    </script>
    

    3、v-debounce

    背景:

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

    需求:

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

    思路:

    1. 定义一个延迟执行的方法,如果在延迟时间内再调用该方法,则重新计算执行时间。
    2. 将时间绑定在 click 方法上。
    const debounce = {
      bind: function(el, binding) {
        el.$value = binding.value
        // 定义计时器容器
        let timer = null
        el.handler = () => {
          if (typeof el.$value !== 'function') {
            throw 'callback must be a function'
          }
          // 取消计时器
          if (timer) {
            clearTimeout(timer)
            timer = null
          }
          // 创建计时器 (1秒后执行函数)
          if (timer === null) {
            timer = setTimeout(() => {
              // 运行函数
              el.$value()
            }, 1000)
          }
        }
        // 添加事件监听器
        el.addEventListener('keyup', el.handler)
      },
      // 当传进来的值更新的时候触发
      componentUpdated: function(el, binding) {
        el.$value = binding.value
      },
      // 指令与元素解绑的时候,移除事件绑定
      unbind(el) {
        el.removeEventListener('keyup', el.handler)
      }
    }
    
    export default debounce
    
    // <input type="text" v-debounce="debounce">
    

    使用:

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

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

    4、v-spare

    需求:

    img请求加载不到网络图片后,新给img备用图片地址

    思路:

    1. 监听img的error事件
    2. img加载错误后触发error事件,检验备用地址是否有效
    3. 如果备用有效,将备用地址赋值给img的src
    const spare = {
      bind(el, binding) {
        el.$value = binding.value
        el.handler = () => {
          // 动态创建 img 标签
          const img = document.createElement('img')
          // 检验备用地址是否有效
          img.onload = () => {
            // 赋值新的图片地址
            el.src = el.$value
          }
          img.onerror = () => {
            console.log('备用图片无法加载')
          }
          img.src = el.$value
        }
        // 绑定事件
        el.addEventListener('error', el.handler)
      },
      // 当传进来的值更新的时候触发
      componentUpdated(el, binding) {
        el.$value = binding.value
      },
      // 指令与元素解绑的时候,移除事件绑定
      unbind(el) {
        el.removeEventListener('error', el.handler)
      }
    }
    
    export default spare
    
    // <img :src="imgScr" v-spare="spareImg" />
    

    使用:

    给 Dom 加上 v-spare 及备用的图片地址即可

    <template>
      <div>
        <img src="http://www.xxx.com/" v-spare="spareImg" />
      </div>
    </template>
    <script>
    export default {
      props: {},
      data() {
        return { spareImg: require('@/assets/logo.png') }
      }
    }
    </script>
    

    copy、longpress、debounce均为参考别人代码后补充完善

    附源码地址:https://github.com/Michael-lzg/v-directives


    起源地下载网 » 分享几个Vue 自定义指令(修改版)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元