[Vue custom command]

Vue custom directive

In the past, I seldom looked at some relatively seldom-used documents in vue at work. I noticed a vue custom command in the past few days and found it very interesting. I hereby record some common custom command packaging and usage. Refer to the document vue customization instruction .

Registration and parameter details:

Register a global custom directive:

Vue.directive('throttle', {
    
    
  // 钩子函数,bind只调用一次,指令第一次绑定到元素时调用
  bind: function (el, binding) {
    
    
  	// 这里处理你想做的事
  }
})

Register a local custom directive in the component:

 directives:{
    
    
    throttle:{
    
    
  	  // 钩子函数,bind只调用一次,指令第一次绑定到元素时调用
      bind: (el, binding) => {
    
    
      	// 这里处理你想做的事
      }
    }
  },

Custom command hook function:

bind: Called only once, when the directive is bound to the element for the first time. One-time initialization settings can be performed here.
inserted: Called when the bound element is inserted into the parent node (only the parent node is guaranteed to exist, but not necessarily inserted into the document).
update: Called when the VNode of the component is updated, but it may happen before the update of its child VNode.
componentUpdated: Called after the VNode of the component where the command is located and its child VNodes are all updated.
unbind: Called only once, when the instruction is unbound from the element.

Hook function parameters:

  1. el: The element bound to the instruction, which can be used to directly manipulate the DOM.
  2. binding: An object, including the following properties:
    name: The command name, excluding the v- prefix.
    value: the binding value of the directive, for example: in v-my-directive="1 + 1", the binding value is 2.
    oldValue: The previous value bound by the directive, only available in update and componentUpdated hooks. Available whether or not the value has changed.
    expression: Instruction expression in string form. For example, in v-my-directive="1 + 1", the expression is "1 + 1".
    arg: Arguments passed to the command, optional. For example, in v-my-directive:foo, the parameter is "foo".
    modifiers: An object containing modifiers. For example: in v-my-directive.foo.bar, the modifier object is { foo: true, bar: true }.
  3. vnode: The virtual node generated by Vue compilation. Move to VNode API for more details.
  4. oldVnode: the previous virtual node, only available in update and componentUpdated hooks.

Custom directive:

v-throttle ======= throttling

Here is an explanation of the similarities and differences between anti-shake and throttling, although both are to prevent multiple executions of events within a unified system, consuming browser performance.
But throttling only triggers one event per unit time, and anti-shake only triggers the last event per unit time.

Application scenario:
throttling, the button is triggered multiple times, so that it is only triggered once per unit time.
Anti-shake, input type trigger or window trigger resize, so that it only triggers the last time per unit time.

Instruction package:

const throttle = {
    
    
  bind: (el, binding) => {
    
    
    // console.log('指令与元素绑定')
    let {
    
     throttleFunc, onThrottle, timeout } = binding.value||{
    
    }
    timeout = timeout || 2 * 1000 
    let throttleTimer;
    el.addEventListener('click', event => {
    
    
      if (!throttleTimer) {
    
    
        if(throttleFunc)throttleFunc() // 节流取消或者第一次触发时的事件
        throttleTimer = setTimeout(() => {
    
    
          throttleTimer = null;
        }, timeout);
      } else {
    
    
        console.warn('处于节流,无法触发点击事件,防误触时间间隔:'+timeout+' ms')
        if(onThrottle)onThrottle() // 触发节流时触发的事件
        event && event.stopImmediatePropagation();
      }
    }, true);
  },
  unbind(el) {
    
    
    // console.warn('指令与元素解绑')
    el.removeEventListener('click', el.handler)
  }
}
export default throttle

Directives.js imports:

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

main.js global import:

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

In-component directives use:

<template>
  <div class="hello">
    <button v-throttle="options">Vue全局注册自定义指令</button><br/>
  </div>
</template>
<script>
export default {
      
      
  data(){
      
      
    return{
      
      
      options: {
      
      
        onThrottle: null, // 节流时触发的事件
        throttleFunc:()=>{
      
      
          this.throttleTest()
        },
        timeout: 200
      }
    }
  },
  methods:{
      
      
    throttleTest(){
      
      
      console.log('触发了一个事件')
    }
  }
}
</script>
<style scoped>
.hello{
      
      
  text-align: left;
  margin: 15% 20%;
  line-height: 40px;
}
</style>

ps: v-throttle="options" is bound here as a parameter, so you can pass data according to your own needs, for example, v-throttle="throttleTest", bind a func, and then change the processing in throttle.js function.

 bind: (el, binding) => {
    
    
    let throttleFunc = binding.value||null // 此时值为传入的 throttleTest
    let timeout = timeout || 2 * 1000 
    let throttleTimer;
    el.addEventListener('click', event => {
    
    
      if (!throttleTimer) {
    
    
        if(throttleFunc)throttleFunc() // 节流取消或者第一次触发时的事件
        throttleTimer = setTimeout(() => {
    
    
          clearTimeout(throttleTimer)
          throttleTimer = null;
        }, timeout);
      } else {
    
    
        console.warn('处于节流,无法触发点击事件,防误触时间间隔:'+timeout+' ms')
        event && event.stopImmediatePropagation();
      }
    }, true);
  }

v-debounce ======= debounce

Application scenario:
input is triggered, so that it only triggers the last time per unit time.

Instruction package:

let debounceFunc,timeout,debounceTimer
let executeFunc = async function(){
    
    
  debounceTimer = await setTimeout(() => {
    
    
    if(debounceFunc)debounceFunc()
  }, timeout);
}
let clearTimer = function(){
    
    
  clearTimeout(debounceTimer)
  debounceTimer = null;
}
const debounce = {
    
    
  bind: (el, binding) => {
    
    
    let val = binding.value||{
    
    }
    debounceFunc = val.debounceFunc
    timeout = val.timeout || 3 * 1000
    el.addEventListener('input', () => {
    
    
      if (debounceTimer) {
    
    
        console.warn('先清除上次还未执行的延时事件')
        clearTimer()
        executeFunc()
      }else{
    
    
        executeFunc()
      }
    }, true);
  },
  unbind(el) {
    
    
    el.removeEventListener('input', el.handler)
  }
}
export default debounce

In-component directives use:

<template>
  <div class="hello">
    <input v-debounce="debounceOptions" v-model="val">
  </div>
</template>
<script>
export default {
      
      
  data(){
      
      
    return{
      
      
      debounceOptions: {
      
      
        debounceFunc:()=>{
      
      
          this.debounceTest()
        },
        timeout: 2 * 1000
      },
      val: ''
    }
  },
  methods:{
      
      
    debounceTest(){
      
      
      console.log('触发了一个事件')
    }
  }
}
</script>
<style scoped>
.hello{
      
      
  text-align: left;
  margin: 15% 20%;
  line-height: 40px;
}
</style>

v-draggable ======= drag

Application scenario:
Some custom pop-up windows cover the content behind, and dragging and dropping can make users easily read the covered content.

Instruction package:

const draggable = {
    
    
  inserted: function (el, binding) {
    
    
    let bodyClientWidth = parseInt(document.body.clientWidth)
    let bodyClientHeight = parseInt(document.body.clientHeight)
    let dragMoveReal = el
    let dialogTop = binding.value || '15vh'
    let dragMoveRealWidth = parseInt(dragMoveReal.style.width)
    if (dragMoveReal.style.width && dragMoveReal.style.width.indexOf) {
    
    
    // 这里是避免dragMoveReal获取的宽度为rem或者%定义的宽度
      if (dragMoveReal.style.width.indexOf('rem') != -1) {
    
    
        dragMoveRealWidth = parseInt(dragMoveReal.style.width) * 100
      } else if (dragMoveReal.style.width.indexOf('%') != -1) {
    
    
        dragMoveRealWidth = parseInt(dragMoveReal.style.width) / 100 * bodyClientWidth
      }
    }
    let dialogLeft = (bodyClientWidth - dragMoveRealWidth) / 2
    dragMoveReal.style.cursor = 'move'
    dragMoveReal.style.position = 'fixed'
    dragMoveReal.style.left = dialogLeft + 'px'
    dragMoveReal.style.top = dialogTop
    dragMoveReal.style.margin = '0'
    dragMoveReal.onmousedown = function (e) {
    
    
      let disx = e.pageX - dragMoveReal.offsetLeft
      let disy = e.pageY - dragMoveReal.offsetTop
      document.onmousemove = function (e) {
    
    
        let x = e.pageX - disx
        let y = e.pageY - disy
        let maxX = bodyClientWidth - parseInt(window.getComputedStyle(dragMoveReal).width)
        let maxY = bodyClientHeight - parseInt(window.getComputedStyle(dragMoveReal).height)
        if (x < 0) {
    
    
          x = 0
        } else if (x > maxX) {
    
    
          x = maxX
        }

        if (y < 0) {
    
    
          y = 0
        } else if (y > maxY) {
    
    
          y = maxY
        }
        dragMoveReal.style.left = x + 'px'
        dragMoveReal.style.top = y + 'px'
      }
      document.onmouseup = function () {
    
    
        document.onmousemove = document.onmouseup = null
      }
    }
  }
}
export default draggable

In-component directives use:

<template>
  <div class="hello">
    <div v-draggable class="block"></div>
  </div>
</template>
<script></script>
<style>
html,body{
      
      
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}
.hello{
      
      
  text-align: left;
  margin: 15% 20%;
  line-height: 40px;
}
.block{
      
      
  width: 300px;
  height: 300px;
  background: red;
}
</style>

Guess you like

Origin blog.csdn.net/weixin_42927679/article/details/125692604