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.
Batch register directives, new directives/index.js file
Import copy from './copy' import longpress from './longpress' // Self-defined const directives = {copy, longpress, } export default { install(Vue) { Object.keys(directives).forEach((key) => { Vue.directive(key, directives[key]) }) }, }Copy the code
Import and call in main.js
import Vue from 'vue'
import Directives from './JS/directives'
Vue.use(Directives)
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 instruction
v-copy
- Long according to the instruction
v-longpress
- Input box anti – shake command
v-debounce
- Prohibit emoticons and special characters
v-emoji
- Lazy loading of images
v-LazyLoad
- Permission check instruction
v-premission
- Implementing page watermarking
v-waterMarker
- Drag and drop the instructions
v-draggable
v-copy
Requirements: achieve one key copy text content, used for right mouse paste.
Ideas:
- Dynamically create
textarea
Tag and setreadOnly
Properties and move out of viewable area - The value to be copied is assigned to
textarea
Of the labelvalue
Property and insert intobody
- The selected value
textarea
And copy - will
body
Insert thetextarea
remove - The event is bound on the first invocation and removed on unbinding
const copy = { bind(el, { value }) { el.$value = value el.handler = () => { if (! El.$value) {// Give a hint when the value is empty. Console. log(' no copy ') return} // create textarea tag dynamically const textarea = document.createElement('textarea') // add the Textarea is set to readonly to prevent iOS from automatically evoking the keyboard, Textarea.readonly = 'readOnly' textarea.style.position = 'absolute' Textarea.style. left = '-9999px' Textarea. Value = el.$value // Insert the textarea into the body Document. The body. The appendChild (textarea) / / selected values and Copy the textarea. Select () const result = document. ExecCommand (' Copy ') if (result) {the console. The log (' copy success ') / / according to project the UI design carefully} document. Body. RemoveChild (textarea)} / / bind click event, El.addeventlistener ('click', el.handler)} // componentUpdated(el, {value}) {el.$value = value}, Unbind (el) {el.removeEventListener('click', el.handler)},} export default CopyCopy the code
Use: add v-copy and copied text to the Dom
<template> <button v-copy="copyText"> copy </button> </template> <script> export default {data() {return {copyText: 'a copy directives', } }, } </script>Copy the code
v-longpress
Requirement: To implement 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
- Triggered when the user presses the button
mousedown
Event to start the timer; Called when the user releases the buttonmouseout
Events. - if
mouseup
If the 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.
- Think about it on mobile
touchstart
.touchend
The event
const longpress = { bind: function (el, binding, vNode) { if (typeof binding.value ! == 'function') {throw 'callback must be a function'} // create a timer (after 2 seconds) let start = (e) => { if (e.type === 'click' && e.button ! == 0) { return } if (pressTimer === null) { pressTimer = setTimeout(() => { handler() }, 2000)}} // Let cancel = (e) => {if (pressTimer! == null) {clearTimeout(pressTimer) pressTimer = null}} // Run const Handler = (e) => {binding.value(e)} // Add event listener AddEventListener ('mousedown', start) el.adDeventListener (' touchStart ', start) cancel) el.addEventListener('mouseout', cancel) el.addEventListener('touchend', AddEventListener (' touchCancel ', cancel)}, // componentUpdated(el, {value}) {el.$value = value}, Unbind (el) {el.removeEventListener('click', el.handler)},} export default longpressCopy 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 () {alert(' longpress ')}}} </script>Copy the code
v-debounce
Background: In the development, some submit and save buttons are sometimes clicked several times in a short period of 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 several 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 clicking to one 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 = {
inserted: function (el, binding) {
let timer
el.addEventListener('keyup', () => {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
binding.value()
}, 1000)
})
},
}
export default 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
v-emoji
Background: In the development of forms, there are often restrictions on input content, for example, expressions and special characters cannot be entered, only numbers or letters can be entered.
Our normal approach is to do this on the on-change event of each 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, the replace (reg, ' ')},,}} < / script >Copy the code
This is a lot of code and difficult to maintain, so we need to define a custom instruction to solve this problem.
Requirements: Design custom instructions for processing form input rules based on regular expressions. The following uses expressions 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 according to demand custom var regRule = / [^ u4E00 - u9FA5 | | d | a zA - Z RNS,..?!,.?! ... - & $= () - + / * {} []] let $inp | s/g = 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 emojiCopy the code
Use: Add v-emoji to the input box that needs verification
<template>
<input type="text" v-model="note" v-emoji />
</template>
Copy the code
v-LazyLoad
Background: In e-commerce projects, there are often a large number of pictures, such as banner advertisement picture, menu navigation picture, meituan and other business list head picture, etc. The large number of pictures and the large size of pictures often affect the page loading speed, resulting in a bad user experience, so it is imperative to optimize the lazy loading of pictures.
Requirement: implement an image lazy loading command, only load the browser visible area of the image.
Ideas:
- The principle of picture lazy loading is mainly to judge whether the current picture has reached the visual area of the core logic
- Take all the image Dom and walk through each image to determine if the current image is in the viewable range
- If it arrives, set the picture
src
Property, otherwise the default image is displayed
Lazy image loading can be implemented in two ways: binding srCOLl event for monitoring, and using IntersectionObserver to judge whether an image reaches the visible area, but there are compatibility problems with browsers.
The following package contains a lazy loading command compatible with both methods to determine whether the browser supports IntersectionObserver API. If so, IntersectionObserver can be used to implement lazy loading; otherwise, SRCOLl event monitoring + throtting method is used to implement lazy loading.
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) } }, }) }, {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)}, // listenerScroll(el) {const handler = lazyload.load (lazyload.load, 300) LazyLoad.load(el) window.addEventListener('scroll', () => { handler(el) }) }, / / load the real images load (el) {const windowHeight = document. The 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 LazyLoadCopy the code
Change the SRC tag inside the component to V-lazyLoad
<img v-LazyLoad="xxx.jpg" />
Copy the code
v-permission
Background: in some background management system, we may need to undertake some actions according to user role permissions, many times we are rudely to – if an element to add v/v – show to show hidden, but you need to determine if judgment conditions cumbersome and a number of places and code this way not only elegant and redundancy. In this case, we can handle it through global custom instructions.
Requirement: customize a permission directive to show and hide Dom that requires permission judgment.
Ideas:
- Customize an array of permissions
- Determine if the user’s permissions are in this array, if so display, otherwise remove the Dom
function checkArray(key) { let arr = ['1', '2', '3', '4'] let index = arr.indexof (key) if (index > -1) {return true // permission} else {return false // no permission}} const Permission = { inserted: function (el, Binding) {let permission = binding. Value // Obtain the value of v-permission if (permission) {let hasPermission = checkArray(permission) if (! HasPermission) {/ / no permissions remove Dom elements el parentNode && el. ParentNode. RemoveChild (el)}}},} export default permissionCopy the code
Use: assign v-permission
<div class="btns"> <! - show - > < button v - permission = "' 1 '" > button 1 < / button > <! - don't show - > < button v - permission = "' 10" > permissions button 2 < / button > < / div >Copy the code
vue-waterMarker
Need: Add a background watermark to the entire page
Ideas:
- use
canvas
Features generatedbase64
Format the picture file, set its font size, color and so on. - Set it as a background image to achieve a page or component watermark effect
Function addWaterMarker(STR, parentNode, font, textColor) {// 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, TextBaseline = 'Middle' (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 waterMarkerCopy the code
Use, set watermark copy, color, font size
<template> <div v-watermarker ="{text:' RZG (180, 180, 180, 0.4)'}"></div> </template>Copy the code
v-draggable
Requirement: Implement a drag-and-drop instruction that can drag and drop elements anywhere in the viewable area of the page.
Ideas:
- Sets the element to be dragged to a relative position and its parent to an absolute position.
- The mouse click
(onmousedown)
Records the current value of the target elementleft
和top
Value. - The mouse moves
(onmousemove)
Calculate the change value of transverse distance and longitudinal distance of each movement, and change the element’sleft
和top
值 - Release the mouse
(onmouseup)
To complete a drag
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
Copy the code
Use: add v-draggable to Dom
<template>
<div class="el-dialog" v-draggable></div>
</template>
Copy the code