8 very practical Vue custom instructions

  • 2021-10-13 06:38:08
  • OfStack

This article is included in github github.com/Michael-lzg …

demo Source Address github. com/Michael-lzg …

In Vue, Vue also allows registration of custom instructions in addition to the instructions built in by default for core functions (v-model and v-show). Its value lies in the fact that developers need to operate on ordinary DOM elements in some scenarios.

Vue custom instructions have two ways: global registration and local registration. First, let's look at the way to register global instructions. Register global instructions through Vue. directive (id, [definition]). Then make the Vue. use () call in the entry file.

Batch registration instruction, create new directives/index. js file


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])
  })
 },
}

Imports and calls in main. js


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

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

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

Here are a few practical Vue custom directives

Copy and paste instruction v-copy Long press instruction v-longpress Input box anti-shake instruction v-debounce Prohibit emoticons and special characters v-emoji Picture lazy loading v-LazyLoad Permission verification instruction v-premission Implementation of page watermarking v-waterMarker Drag instruction v-draggable

v-copy

Requirements: Realize 1-key copy of text content for right mouse button paste.

Thoughts:

Create an textarea tag dynamically, set readOnly properties and move out of the visual area The value to be copied is assigned to the value attribute of the textarea tag and inserted into the body Select the value textarea and copy Remove textarea inserted in body Bind events on the first call and remove events on unbind

const copy = {
 bind(el, { value }) {
  el.$value = value
  el.handler = () => {
   if (!el.$value) {
    //  When the value is empty, give a prompt. Can be based on the project UI Carefully design 
    console.log(' No copied content ')
    return
   }
   //  Dynamic creation  textarea  Label 
   const textarea = document.createElement('textarea')
   //  Set the  textarea  Set to  readonly  Prevent  iOS  Automatically call up the keyboard, and at the same time, set the  textarea  Move out of the visible area 
   textarea.readOnly = 'readonly'
   textarea.style.position = 'absolute'
   textarea.style.left = '-9999px'
   //  Will  copy  Assign the value of the  textarea  Labeled  value  Attribute 
   textarea.value = el.$value
   //  Will  textarea  Insert into  body  Medium 
   document.body.appendChild(textarea)
   //  Select the value and copy 
   textarea.select()
   const result = document.execCommand('Copy')
   if (result) {
    console.log(' Successful replication ') //  Can be based on the project UI Carefully design 
   }
   document.body.removeChild(textarea)
  }
  //  Bind the click event, which is called 1 Key  copy  La 
  el.addEventListener('click', el.handler)
 },
 //  Triggered when the incoming value is updated 
 componentUpdated(el, { value }) {
  el.$value = value
 },
 //  Remove the event binding when the instruction is unbound from the element 
 unbind(el) {
  el.removeEventListener('click', el.handler)
 },
}

export default copy

Use: Add v-copy and copied text to Dom


<template>
 <button v-copy="copyText"> Duplicate </button>
</template>

<script> export default {
  data() {
   return {
    copyText: 'a copy directives',
   }
  },
 }
</script>

v-longpress

Requirement: To realize long press, the user needs to press and hold the button for several seconds to trigger the corresponding event

Thoughts:

Create a timer and execute the function in 2 seconds When the user presses the button, the mousedown event is triggered to start the timer; The mouseout event is called when the user releases the button. If the mouseup event is triggered within 2 seconds, the timer is cleared as a normal click event If the timer is not cleared within 2 seconds, it is determined to be a long press, and the associated function can be executed. touchstart and touchend events should be considered on the mobile side

const longpress = {
 bind: function (el, binding, vNode) {
  if (typeof binding.value !== 'function') {
   throw 'callback must be a function'
  }
  //  Defining variables 
  let pressTimer = null
  //  Create a timer (  2 Execute the function in seconds   ) 
  let start = (e) => {
   if (e.type === 'click' && e.button !== 0) {
    return
   }
   if (pressTimer === null) {
    pressTimer = setTimeout(() => {
     handler()
    }, 2000)
   }
  }
  //  Cancel the timer 
  let cancel = (e) => {
   if (pressTimer !== null) {
    clearTimeout(pressTimer)
    pressTimer = null
   }
  }
  //  Running function 
  const handler = (e) => {
   binding.value(e)
  }
  //  Add an event listener 
  el.addEventListener('mousedown', start)
  el.addEventListener('touchstart', start)
  //  Cancel the timer 
  el.addEventListener('click', cancel)
  el.addEventListener('mouseout', cancel)
  el.addEventListener('touchend', cancel)
  el.addEventListener('touchcancel', cancel)
 },
 //  Triggered when the incoming value is updated 
 componentUpdated(el, { value }) {
  el.$value = value
 },
 //  Remove the event binding when the instruction is unbound from the element 
 unbind(el) {
  el.removeEventListener('click', el.handler)
 },
}

export default longpress

Use: Add v-longpress and callback function to Dom


<template>
 <button v-longpress="longpress"> Long press </button>
</template>

<script> 
export default {
 methods: {
  longpress () {
   alert(' Long-term instruction takes effect ')
  }
 }
} 
</script>

v-debounce

Background: In the development, some submit and save buttons are sometimes clicked many times in a short time, which will repeatedly request the back-end interface, resulting in data confusion. For example, if you add a submit button for a new form, you will add multiple duplicate data if you click it many times.

Requirements: Prevent the button from being clicked many times in a short time, and use the anti-shake function to limit the specified time to only click once.

Thoughts:

Defines a method with delayed execution, and recalculates the execution time if the method is called again within the delayed time.
Bind the time to the click method.


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

export default debounce

Use: Add v-debounce and callback function to Dom


<template>
 <button v-debounce="debounceClick"> Anti-shake </button>
</template>

<script> 
export default {
 methods: {
  debounceClick () {
   console.log(' Trigger only 1 Times ')
  }
 }
} 
</script>

v-emoji

Background: Form input encountered in development often has restrictions on input content, such as not inputting expressions and special characters, only inputting numbers or letters, etc.

Our usual approach is to handle the on-change event on every 1 form.


<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/g
    this.note = this.note.replace(reg, '')
   },
  },
 } </script>

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

Requirements: According to regular expressions, design custom instructions to deal with form input rules. The following is an example of prohibiting the input of expressions and special characters.


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|rns,.?! ,. ? ! … &$=()-+/*{}[]]|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: Add v-emoji to the input box to be verified


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

v-LazyLoad

Background: In e-commerce projects, there are often a large number of pictures, such as banner advertisement map, menu navigation map, Meituan and other business list headers. Many pictures and too large pictures often affect the page loading speed and cause bad user experience, so it is imperative to optimize the lazy loading of pictures.

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

Thoughts:

The principle of lazy loading of pictures is mainly realized by the core logic of judging whether the current picture has reached the visual area Get all the pictures Dom, traverse each picture to judge whether the current picture is in the visual area If you arrive, set the src property of the picture, otherwise, the default picture will be displayed

Lazy loading of pictures can be achieved in two ways, 1 is to bind srcoll events to monitor, and 2 is to use IntersectionObserver to judge whether pictures are in the visible area, but there are browser compatibility problems.

The following encapsulation of a lazy loading instruction is compatible with two methods to judge whether the browser supports IntersectionObserver API. If it supports it, use IntersectionObserver to realize lazy loading, otherwise use srcoll event monitoring + throttling.


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

Use, replace the src of the label in the component with v-LazyLoad


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

v-permission

Background: In some background management systems, we may need to judge some operation permissions according to the user role. Most of the time, we rudely add v-if/v-show to an element for display and hiding. However, if the judgment conditions are cumbersome and multiple places need to be judged, the code in this way is not only inelegant but also redundant. In view of this situation, we can deal with it through global custom instructions.

Requirements: Customize a permission instruction to show and hide Dom that needs permission judgment.

Thoughts:

Customize 1 permission array Determine whether the user's permission is in this array, and if so, display it; Otherwise, remove Dom

function checkArray(key) {
 let arr = ['1', '2', '3', '4']
 let index = arr.indexOf(key)
 if (index > -1) {
  return true //  Have authority 
 } else {
  return false //  No authority 
 }
}

const permission = {
 inserted: function (el, binding) {
  let permission = binding.value //  Get to  v-permission Value of 
  if (permission) {
   let hasPermission = checkArray(permission)
   if (!hasPermission) {
    //  No permissions   Remove Dom Element 
    el.parentNode && el.parentNode.removeChild(el)
   }
  }
 },
}

export default permission

Use: Assign value to v-permission


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

vue-waterMarker

Requirement: Add background watermark to the whole page

Thoughts:

Use canvas features to generate base64 format picture file, set its font size, color, etc. Set it as a background image to achieve the watermarking effect of the page or component

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

Use, set the watermark copy, color and font size


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

v-draggable

Requirements: Realize a drag instruction, which can drag and drop elements arbitrarily in the visual area of the page.

Thoughts:

Set the element to be dragged to relative positioning, and its parent element to absolute positioning. Record the current left and top values of the target element when the mouse is pressed (onmousedown). When the mouse moves (onmousemove), the change values of the transverse distance and the longitudinal distance of each move are calculated, and the left and top values of the element are changed Drag and drop completed once when mouse is released (onmouseup)

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

Use: Add v-draggable to Dom


<template>
 <div class="el-dialog" v-draggable></div>
</template>

All instruction source addresses github. com/Michael-lzg …

These are the details of 8 very practical Vue custom instructions. For more information about vue custom instructions, please pay attention to other related articles on this site!


Related articles: