TOP 8 very useful Vue custom instructions

In Vue, in addition to the default built-in instructions (v-model and v-show) of the core functions, Vue also allows the registration of custom instructions. Its value is when developers need to operate on ordinary DOM elements in certain scenarios.

TOP 8 very useful Vue custom instructions

Share 8 very useful Vue custom instructions

This article has been included in github.com/Michael-lzg...

Demo source code address github.com/Michael-lzg...

In Vue, in addition to the default built-in instructions (v-model and v-show) of the core functions, Vue also allows the registration of custom instructions. Its value is when developers need to operate on ordinary DOM elements in certain scenarios.

Vue custom instructions have two methods: global registration and local registration. Let's look at the instruction register globals way, by Vue.directive( id, [definition] )way of registered global directive. Then at the entrance file Vue.use()called.

Bulk registration instruction, the new directives/index.jsfile

import copy from './copy'
import longpress from './longpress'
// Custom instruction
const directives = {
  copy,
  longpress,
}

export default {
  install(Vue) {
    Object.keys(directives).forEach((key) => {
      Vue.directive(key, directives[key])
    })
  },
}

In the main.js introduction and call

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

The instruction definition function provides several hook functions (optional):

  • bind: It is called only once. It is called when the instruction is bound to the element for the first time. You can define an initialization action that is executed once when binding.
  • inserted: Called when the bound element is inserted into the parent node (the parent node can be called, it does not need to exist in the document).
  • update: Called when the template where the bound element is located is updated, regardless of whether the bound value changes. By comparing the binding value before and after the update.
  • componentUpdated: Called when the template of the bound element completes an update cycle.
  • unbind: Only called once, when the instruction is unbound from the element.

Here are a few useful Vue custom instructions to share

  • Copy and paste instructions v-copy
  • Long press command v-longpress
  • Input box anti-shake command v-debounce
  • Emoticons and special characters are prohibited v-emoji
  • Image lazy loading v-LazyLoad
  • Authorization check instruction v-premission
  • Implement page watermark v-waterMarker
  • Drag instruction v-draggable

v-copy

Requirement: Realize one-click copying of text content for right mouse button pasting.

Ideas:

  1. Dynamically created textarea label, and sets readOnly the properties in and out of the visible area
  2. The value assigned to copy textareathe tag valueattributes, and inserted into thebody
  3. Select the values textarea and copy
  4. The body insert is textarea removed
  5. Bind the event on the first call and remove the event when unbinding
const copy = {
  bind(el, {value }) {
    el.$value = value
    el.handler = () => {
      if (!el.$value) {
        // When the value is empty, a prompt is given. Can be carefully designed according to the project UI
        console.log('No copy content')
        return
      }
      // dynamically create textarea tags
      const textarea = document.createElement('textarea')
      // Set the textarea to readonly to prevent the keyboard from being automatically awakened under iOS and move the textarea out of the visible area
      textarea.readOnly ='readonly'
      textarea.style.position ='absolute'
      textarea.style.left ='-9999px'
      // Assign the value to be copied to the value attribute of the textarea tag
      textarea.value = el.$value
      // insert textarea into body
      document.body.appendChild(textarea)
      // select value and copy
      textarea.select()
      const result = document.execCommand('Copy')
      if (result) {
        console.log('Copy successful') // Can be carefully designed according to the project UI
      }
      document.body.removeChild(textarea)
    }
    // Binding the click event is the so-called one-click copy
    el.addEventListener('click', el.handler)
  },
  // Triggered when the value passed in is updated
  componentUpdated(el, {value }) {
    el.$value = value
  },
  // When the instruction is unbound from the element, remove the event binding
  unbind(el) {
    el.removeEventListener('click', el.handler)
  },
}

export default copy

Use: Dom add v-copytext and can be copied

   export default {
     data() {
       return {
         copyText:'a copy directives',
       }
     },
   }

v-longpress

Requirements: To achieve a long press, the user needs to press and hold the button for a few seconds to trigger the corresponding event

Ideas:

  1. Create a timer and execute the function after 2 seconds
  2. When the user presses a button to trigger mousedown the event, starting a timer; invoked when the user releases the button mouseoutevent.
  3. If mouseupthe trigger event within two seconds, the timer is cleared, as an ordinary click event
  4. If the timer is not cleared within 2 seconds, it is judged as a long press and the associated function can be executed.
  5. On the mobile side to consider touchstarttouchendevents
const longpress = {
  bind: function (el, binding, vNode) {
    if (typeof binding.value !=='function') {
      throw'callback must be a function'
    }
    // define variables
    let pressTimer = null
    // Create a timer (execute function after 2 seconds)
    let start = (e) => {
      if (e.type ==='click' && e.button !== 0) {
        return
      }
      if (pressTimer === null) {
        pressTimer = setTimeout(() => {
          handler()
        }, 2000)
      }
    }
    // cancel timer
    let cancel = (e) => {
      if (pressTimer !== null) {
        clearTimeout(pressTimer)
        pressTimer = null
      }
    }
    // Run function
    const handler = (e) => {
      binding.value(e)
    }
    // Add event listener
    el.addEventListener('mousedown', start)
    el.addEventListener('touchstart', start)
    // cancel timer
    el.addEventListener('click', cancel)
    el.addEventListener('mouseout', cancel)
    el.addEventListener('touchend', cancel)
    el.addEventListener('touchcancel', cancel)
  },
  // Triggered when the value passed in is updated
  componentUpdated(el, {value }) {
    el.$value = value
  },
  // When the instruction is unbound from the element, remove the event binding
  unbind(el) {
    el.removeEventListener('click', el.handler)
  },
}

export default longpress

Use: Dom plus v-longpress and a callback function to

export default {
   methods: {
     longpress () {
       alert('Long press the command to take effect')
     }
   }
}

v-debounce

Background: During development, some submit and save buttons are sometimes clicked multiple times in a short period of time, which will repeatedly request the back-end interface multiple times, causing data confusion. For example, the submit button of a new form will be clicked multiple times. Multiple duplicate data will be added.

Requirements: To prevent the button from being clicked multiple times in a short period of time, use the anti-shake function to limit the button to be clicked once within the specified time.

Ideas:

  1. Define a method to delay execution. If the method is called within the delay time, the execution time will be recalculated.
  2. Bind the event to the click method.
const debounce = {
  inserted: function (el, binding) {
    let timer
    el.addEventListener('click', () => {
      if (timer) {
        clearTimeout(timer)
      }
      timer = setTimeout(() => {
        binding.value()
      }, 1000)
    })
  },
}

export default debounce
 

Use: Dom plus v-debounceand a callback function to

export default {
   methods: {
     debounceClick () {
       console.log('Only trigger once')
     }
   }
}

v-emoji

Background: The form input encountered during development often has restrictions on the input content, such as not being able to enter emoticons and special characters, but only numbers or letters.

Our method is conventional in every form of on-changedoing handle on events.

  export default {
    methods: {
      vaidateEmoji() {
        var reg = /[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,。?!…—&$=()-+/*{}[\]]|\s/g
        this.note = this.note.replace(reg, '')
      },
    },
  }

This kind of code is relatively large and difficult to maintain, so we need to customize a command to solve this problem.

Requirements: According to regular expressions, design instructions for custom processing form input rules. The following takes the prohibition of emoticons and special characters as an example.

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) {
     // Regular rules can be customized according to requirements
     var regRule = /[^\u4E00-\u9FA5|\d|\a-zA-Z|\r\n\s,.?!,. ? ! …—&$=()-+/*{}[\]]|\s/g
     let $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

Use: The plus input box needs to be checked v-emojito

v-LazyLoad

Background: In e-commerce projects, there are often a lot of pictures, such as banner advertising pictures, menu navigation pictures, and business listing header pictures such as Meituan. A large number of pictures and a large picture often affect the page loading speed and cause a bad user experience. Therefore, it is imperative to optimize the lazy loading of pictures.

Requirements: Implement a picture lazy loading instruction, only load pictures in the visible area of ​​the browser.

Ideas:

  1. The principle of lazy loading of pictures is mainly to determine whether the current picture has reached the visual area, which is the core logic.
  2. Get all the picture Dom, traverse each picture to determine whether the current picture is within the visible area
  3. If you set the picture srcproperty, otherwise the default image

Pictures lazy loading can be achieved in two ways, one Binding srcollEvents to monitor, the second is to use IntersectionObserverto determine whether the image to the viewing area, but there are browser compatibility issues.

The following package is compatible with a lazy load instruction are two ways to determine whether the browser supports the IntersectionObserverAPI, if supported on the use of IntersectionObserverrealization lazy loading, or use the srcollevent listener method throttling + implementation.

const LazyLoad = {
  // install method
  install(Vue, options) {
    const defaultSrc = options.default
    Vue.directive('lazy', {
      bind(el, binding) {
        LazyLoad.init(el, binding.value, defaultSrc)
      },
      inserted(el) {
        if (IntersectionObserver) {
          LazyLoad.observe(el)
        } else {
          LazyLoad.listenerScroll(el)
        }
      },
    })
  },
  // Initialize
  init(el, val, def) {
    el.setAttribute('data-src', val)
    el.setAttribute('src', def)
  },
  // Use IntersectionObserver to monitor el
  observe(el) {
    var io = new IntersectionObserver((entries) => {
      const realSrc = el.dataset.src
      if (entries[0].isIntersecting) {
        if (realSrc) {
          el.src = realSrc
          el.removeAttribute('data-src')
        }
      }
    })
    io.observe(el)
  },
  // listen for scroll events
  listenerScroll(el) {
    const handler = LazyLoad.throttle(LazyLoad.load, 300)
    LazyLoad.load(el)
    window.addEventListener('scroll', () => {
      handler(el)
    })
  },
  // Load real picture
  load(el) {
    const windowHeight = document.documentElement.clientHeight
    const elTop = el.getBoundingClientRect().top
    const elBtm = el.getBoundingClientRect().bottom
    const realSrc = el.dataset.src
    if (elTop-windowHeight <0 && elBtm> 0) {
      if (realSrc) {
        el.src = realSrc
        el.removeAttribute('data-src')
      }
    }
  },
  // Throttle
  throttle(fn, delay) {
    let timer
    let prevTime
    return function (...args) {
      const currTime = Date.now()
      const context = this
      if (!prevTime) prevTime = currTime
      clearTimeout(timer)

      if (currTime-prevTime> delay) {
        prevTime = currTime
        fn.apply(context, args)
        clearTimeout(timer)
        return
      }

      timer = setTimeout(function () {
        prevTime = Date.now()
        timer = null
        fn.apply(context, args)
      }, delay)
    }
  },
}

export default LazyLoad

In use, the inner assembly tag srcintov-LazyLoad

v-permission

Background: In some backstage management system, we may need some action based on user roles determine permissions, many times we are rudely to add an element v-if / v-showto display hidden, but if the judgment conditions are cumbersome and require more than one place, that this The code in this way is not only inelegant but also redundant. In view of this situation, we can handle it through global custom instructions.

Requirement: Customize a permission instruction to display and hide the Dom that needs permission judgment.

Ideas:

  1. Customize an array of permissions
  2. Determine whether the user's permission is in this array, if it is, display it, otherwise remove the Dom
function checkArray(key) {
   let arr = ['1', '2', '3', '4']
   let index = arr.indexOf(key)
   if (index> -1) {
     return true // have permission
   } else {
     return false // No permission
   }
}

const permission = {
   inserted: function (el, binding) {
     let permission = binding.value // get the value of v-permission
     if (permission) {
       let hasPermission = checkArray(permission)
       if (!hasPermission) {
         // No permission to remove Dom element
         el.parentNode && el.parentNode.removeChild(el)
       }
     }
   },
}

export default permission

Use: to v-permission assign judges to

vue-waterMarker

Requirements: Add a background watermark to the entire page

Ideas:

  1. Use canvasproperties to create a base64picture file format, set the font size, color, and so on.
  2. Set it as a background image to achieve the watermark effect of the page or component
function addWaterMarker(str, parentNode, font, textColor) {
   // Watermark text, parent element, font, text color
   var can = document.createElement('canvas')
   parentNode.appendChild(can)
   can.width = 200
   can.height = 150
   can.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

To use, set the watermark copy, color and font size

v-draggable

Requirements: To implement a drag-and-drop instruction, you can drag and drop elements in the visible area of ​​the page.

Ideas:

  1. Set the element to be dragged as relative positioning, and its parent element as absolute positioning.
  2. Mouse Down (onmousedown)recording current target element leftand topvalues.
  3. Mouse movement (onmousemove)calculation transverse distance variation value and the vertical distance moved each time, and change the elements leftand topvalues
  4. (onmouseup)Complete a drag when the mouse is released
const draggable = {
  inserted: function (el) {
    el.style.cursor = 'move'
    el.onmousedown = function (e) {
      let disx = e.pageX - el.offsetLeft
      let disy = e.pageY - el.offsetTop
      document.onmousemove = function (e) {
        let x = e.pageX - disx
        let y = e.pageY - disy
        let 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

Use: Just add v-draggable to Dom

What's Your Reaction?

like
0
dislike
0
love
0
funny
0
angry
0
sad
0
wow
0