Source | github.com/Michael-lzg… The day before yesterday, I saw someone on Github sharing that the optimized version of Vue custom instruction was adjusted according to the actual business needs. Don’t spray if you don’t like it.
In Vue, in addition to the core functionality of the default built-in directives (V-model and V-show), Vue also allows the registration of custom directives. It is useful when developers need to manipulate ordinary DOM elements in certain scenarios.
Vue custom commands can be registered globally or locally. Directive (id, [definition]) {var. Directive (id, [definition]); The vue.use () call is then made in the entry file.
Import and call in main.js
import Directives from './directives'
Vue.use(Directives)
Copy the code
Batch register directives, new directives/index.js file
import copy from './copy'
import longpress from './longpress'
import debounce from './debounce'
import spare from './spare'
// Custom instruction
const directives = {
copy, // Copy and paste instructions
longpress, // Hold the command
debounce, // Enter box anti-shake command
spare // Image loading failed to display the standby image command
}
export default {
install(Vue) {
Object.keys(directives).forEach(key= > {
Vue.directive(key, directives[key])
})
}
}
Copy the code
The instruction definition function provides several hook functions (optionally) :
- Bind: called once, when the directive is first bound to an element. You can define an initialization action that is performed once at binding time.
- Inserted: Called when the bound element is inserted into its parent (the parent is called if it exists, not in the document).
- Update: Called when the template to which the element is bound is updated, regardless of whether the binding value changes. By comparing the binding values before and after the update.
- ComponentUpdated: Called when the template to which the bound element belongs completes an update cycle.
- Unbind: Called only once, when an instruction is unbound from an element.
A few useful Vue custom directives are shared below
- Copy and paste instructions V-copy
- Longpress command v-longpress
- Enter the box anti-shock command V-debounce
- Picture load failure display spare picture command V-spare
1, v – copy
Requirements:
One key copy text content, used for right mouse paste, some scenes may need to have a callback.
Ideas:
- Dynamically create the TextaREA tag and set the readOnly property and move out of the viewable area
- The value to be copied is assigned to the value property of the Textarea tag and inserted into the body
- Select the value Textarea and copy it
- Remove the textarea inserted in the body
- The event is bound on the first invocation and removed on unbinding
const copy = {
bind(el, binding) {
el.$value = binding.value
el.handler = () = > {
// If the value is null, a prompt will be given
if (typeofel.$value ! = ='object') {
if(! el.$value) {// Can be based on the project UI design default prompt
console.log('No copy content')
return}}else {
if(! el.$value.text) {// Execute the callback function if there is a callback
el.$value.callback(false)
return}}// Create textarea tag dynamically
const textarea = document.createElement('textarea')
// Set this textarea to readonly to prevent iOS from automatically evoking the keyboard and remove the Textarea out of view
textarea.readOnly = 'readonly'
textarea.style.position = 'absolute'
textarea.style.left = '-9999px'
// Assign the copy value to the textarea tag's value property
if (typeofel.$value ! = ='object') {
textarea.value = el.$value
} else {
textarea.value = el.$value.text
}
// Insert the textarea into the body
document.body.appendChild(textarea)
// Select the value and copy it
textarea.select()
const result = document.execCommand('Copy')
if (typeofel.$value ! = ='object') {
if (result) {
// Copy success according to the project UI design default prompt
console.log('Copy successful')}else {
// Failure to copy can be prompted by the project UI design default
console.log('Replication failed')}}else {
if (result) {
// Execute the callback function if there is a callback after replication
el.$value.callback(true)}else {
// If there is a callback for replication failure, execute the callback function
el.$value.callback(false)}}document.body.removeChild(textarea)
}
// Add event listeners
el.addEventListener('click', el.handler)
},
// Triggered when the value passed in is updated
componentUpdated(el, binding) {
el.$value = binding.value
},
// Remove event bindings when directives are unbound from elements
unbind(el) {
el.removeEventListener('click', el.handler)
}
}
export default copy
// Pass String without a callback function
// <div v-copy="'复制内容'">复制按钮</div>
// Pass the Object callback callback parameter Boolean true successful false failed
//
Copy the code
Use:
Add v-copy and copied text to the Dom
<template> <div> <div v-copy="copyText"> </div> <div v-copy="{text: copyText, callback: CopyCallback}"> copy </div> </div> </template> <script> export default {data() {return {copyText: 'copy'}}, methods: { copyCallback(CallbackVal) { console.log(CallbackVal) } } } </script>Copy the code
2, v – longpress
Requirements:
To achieve long press, the user needs to press and hold the button for several seconds to trigger the corresponding event
Ideas:
- Create a timer and execute the function after 2 seconds
- The mouseDown event is triggered when the user presses the button, starting 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 does not clear within 2 seconds, it is considered a long press and the associated function can be executed.
- On the mobile side you have to consider the TouchStart, TouchEnd events
const longpress = {
bind: function(el, binding) {
el.$value = binding.value
// Define the timer container
let timer = null
// Create timer (execute function after 2 seconds)
el.start = e= > {
if (typeofel.$value ! = ='function') {
throw 'callback must be a function'
}
if (e.type === 'click'&& e.button ! = =0) {
return
}
if (timer === null) {
timer = setTimeout(() = > {
// Run the function
el.$value()
}, 2000)}}// Cancel the timer
el.cancel = () = > {
if(timer ! = =null) {
clearTimeout(timer)
timer = null}}// Add event listeners
el.addEventListener('mousedown', el.start)
el.addEventListener('touchstart', el.start)
// Cancel the timer
el.addEventListener('click', el.cancel)
el.addEventListener('mouseout', el.cancel)
el.addEventListener('touchend', el.cancel)
el.addEventListener('touchcancel', el.cancel)
},
// Triggered when the value passed in is updated
componentUpdated(el, binding) {
el.$value = binding.value
},
// Remove event bindings when directives are unbound from elements
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
longpress
Copy the code
Use:
Add v-longpress and the callback function to the Dom
<template> <button v-longpress="longpress"> </button> </template> <script> export default {methods: {longpress() {console.log(' longpress to effect ')}}} </script>Copy the code
3, v – debounce
Background:
In the development, some submit and save buttons are sometimes clicked for many times in a short time, which will repeatedly request the back-end interface and cause data confusion. For example, when the submit button of a new form is clicked for many times, multiple repeated 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 one click in a specified period of time.
Ideas:
- Define a method that delays execution, and recalculate the execution time if the method is called within the delay time.
- Bind the time to the click method.
const debounce = {
bind: function(el, binding) {
el.$value = binding.value
// Define the timer container
let timer = null
el.handler = () = > {
if (typeofel.$value ! = ='function') {
throw 'callback must be a function'
}
// Cancel the timer
if (timer) {
clearTimeout(timer)
timer = null
}
// Create timer (execute function after 1 second)
if (timer === null) {
timer = setTimeout(() = > {
// Run the function
el.$value()
}, 1000)}}// Add event listeners
el.addEventListener('keyup', el.handler)
},
// Triggered when the value passed in is updated
componentUpdated: function(el, binding) {
el.$value = binding.value
},
// Remove event bindings when directives are unbound from elements
unbind(el) {
el.removeEventListener('keyup', el.handler)
}
}
export default debounce
// <input type="text" v-debounce="debounce">
Copy the code
Use:
Add v-debounce and a callback to the Dom
<template> <button v-debounce="debounceClick"> </button> </template> <script> export default {methods: {debounceClick() {console.log(' only triggered once ')}}} </script>Copy the code
4, v – spare
Requirements:
When the img request fails to load a network image, a new image address is given to img
Ideas:
- Listen for img error events
- An error event is raised after the img load error to verify that the alternate address is valid
- If the alternate address is available, assign the alternate address to the SRC of img
const spare = {
bind(el, binding) {
el.$value = binding.value
el.handler = () = > {
// Dynamically create an IMG tag
const img = document.createElement('img')
// Verify that the alternate address is valid
img.onload = () = > {
// Assign a new image address
el.src = el.$value
}
img.onerror = () = > {
console.log('Backup image cannot load')
}
img.src = el.$value
}
// Bind events
el.addEventListener('error', el.handler)
},
// Triggered when the value passed in is updated
componentUpdated(el, binding) {
el.$value = binding.value
},
// Remove event bindings when directives are unbound from elements
unbind(el) {
el.removeEventListener('error', el.handler)
}
}
export default spare
// <img :src="imgScr" v-spare="spareImg" />
Copy the code
Use:
Add v-spare and spare image addresses to Dom
<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 the code
Copy, Longpress, and Debounce are all based on other people’s code
Attached source code address: github.com/Michael-lzg…