Preparation
Create directory file: src/directive/
import copy from "./copy"; import longpress from "./longpress" import debounce from "./debounce"; import emoji from "./emoji"; import LazyLoad from "./LazyLoad"; import permission from "./permission"; import waterMarker from "./waterMarker"; import draggable from "./draggable"; import empty from "./empty"; const directives = { copy,longpress,debounce,emoji,LazyLoad,permission,waterMarker,draggable,empty }; export default { install(Vue) { (directives).forEach((key) => { (key, directives[key]); }); }, };
Then introduce and register
// Introduce global custom commandsimport directive from './directive' (directive)
Comment: Custom directive hook function
- bind: Only once, the command is used when the first time it is bound to an element, you can use this hook function to define an initial action that is executed once during binding
- inserted: Called when the bound element is inserted into the parent node (it can be called if the parent node exists, and 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 binding value changes, by comparing the changes before and after the update, unnecessary updates can be ignored.
- componentUpdated: Called when the template where the bound element is located has completed an update cycle
- unbind: Only called once, when the directive is unbined with the element
Notes on the parameters of the hook function:
- el: The element bound to the instruction can be used to directly operate the DOM
- binding: An object containing the following properties:
- name: directive name, excluding v-prefix
- value: The binding value of the instruction, this value is passed in when used. If it is a reference, it will be copied directly. If it is a function, it can be called directly.
- oldValue: The previous value bound by the directive is only available in update and componentUpdated, and is available regardless of whether the value changes or not.
- expression: the expression or variable name of the bound value,
- arg: The parameter passed to the instruction, for example, v-my-directive: foo, the value of arg is "foo"
- modifiers: an object containing modifiers. For example, the value of modifier object modifiers is {foo:true, bar:true}
- vnode: Vue compiles the generated virtual node.
- oldVnode: Previous virtual node, available only in update and componentUpdated hooks.
Except for el, all other parameters should be read-only and must not be modified.
One-click copy
Requirements: Implement one-click copying of text content, used for right-click pasting
Ideas:
Dynamically create textarea tags, set readOnly attribute and move out of the visual area
Assign the value to be copied to the value property of the textarea tag and insert it into the body
Select the value textarea and copy
Remove the textarea inserted in the body
Binding events on the first call, removing events when unbinding
export default { bind(el, { value }) { el.$value = value; = () => { if (!el.$value) { // When the value is empty, give a prompt. Can be carefully designed according to the project UI ("No copy content"); return; } // Dynamically create textarea tags const textarea = ("textarea"); // Set the textarea to readonly to prevent the keyboard from being automatically evoked under iOS, and to move the textarea out of the visual area = "readonly"; = "absolute"; = "-9999px"; // Assign the value to copy to the value attribute of the textarea tag = el.$value; // Insert textarea into body (textarea); // Select the value and copy (); const result = ("Copy"); if (result) { ("Copy successfully"); // Can be carefully designed according to the project UI } (textarea); }; // // Bind click event is the so-called one-click copy ("click", ); }, // Triggered when the value passed in is updated componentUpdated(el, { value }) { el.$value = value; }, // When the command is unbinded with the element, remove the event binding unbind(el) { ("click", ); }, };
<button v-copy="copyContent">Click to copy</button> data(){ return { copyContent: "Copyed information", } }
One-click long press
need:
Implement long pressing, the user needs to press and hold the button for a few 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, and the timer is started; the mouseout event is called when the user releases the button.
If the mouseup event is triggered within 2 seconds, clear the timer as a normal click event
If the timer is not cleared within 2 seconds, it is determined that a long press can be used to execute the associated function.
On mobile, you should consider touchstart and touchend events
export default { bind: function (el, binding, vNode) { //Judge type if (typeof !== "function") { throw "callback must be a function"; } // Define variables let pressTimer = null; // Create a timer and execute it after two seconds let start = (e) => { if ( === "click" && !== 0) { return; } if (pressTimer === null) { pressTimer = setTimeout(() => { handler(); }, 2000); } }; // Cancel the timer let cancel = (e) => { if (pressTimer !== null) { clearTimeout(pressTimer); pressTimer = null; } }; // Run the function const handler = (e) => { (e); }; // Add event listener ("mousedown", start); ("touchstart", start); // Cancel the timer ("click", cancel); ("mouseout", cancel); ("touchend", cancel); ("touchcancel", cancel); }, // Triggered when the value passed in is updated componentUpdated(el, { value }) { el.$value = value; }, // When the command is unbinded with the element, remove the event binding unbind(el) { ("click", ); }, };
<!-- longpress Must be a function --> <button v-longpress="longpress">Press for two seconds</button>
Anti-shake
need:
Prevent the button from being clicked multiple times in a short period of time, and use the anti-shake function to limit the number of clicks only once within the specified time.
Ideas:
Define a method that delays execution. If the method is called again within the delay time, the execution time will be recalculated.
Bind time to the click method.
export default { inserted: function(el,binding) { let timer; ("click", () => { if(timer) { clearTimeout(timer) } timer = setTimeout(() => { () },1000) }) } }
<!-- Anti-shake --> <button v-debounce="debounceClick">点击Anti-shake</button> debounceClick() { ("It should only be triggered once"); },
Limit input box type
Form input encountered during development often has restrictions on the input content, such as not being able to enter emoticons and special characters, only digits or letters, etc.
Our regular method is to process the on-change event of each form. This is the code volume is relatively large and difficult to maintain
need:
According to regular expressions, design instructions to customize the input rules of form. The following is an example of prohibiting the input of emoticons and special characters.
let findEle = (parent, type) => { return () === type ? parent : (type); }; const trigger = (el, type) => { const e = ("htmlEvents"); (type, true, true); (e); }; export default { bind: function (el, binding, vnode) { // Regular rules can be customized according to requirements // var regRule = /[^u4E00-u9FA5|d|a-zA-Z|rns,.?!,。?!…—&$=()-+/*{}[]]|s/g; var regRule = /[^\u4E00-\u9FA5\dA-Za-z]|/g; // Only enter Chinese, English and numbers let $inp = findEle(el, "input"); el.$inp = $inp; $ = function () { let val = $; $ = (regRule, ""); trigger($inp, "input"); }; $("keyup", $); }, unbind: function (el) { el.$("keyup", el.$); }, };
use
<!-- Limit input types --> <input type="text" v-model="value" v-emoji>
Lazy image loading
Requirements: Implement a lazy image loading instruction, which only loads pictures in the visible area of the browser.
Ideas:
The principle of lazy image loading is mainly to determine whether the current image has reached the visual area.
Get all the pictures Dom and traverse each picture to determine whether the current picture is within the visual area.
If it arrives, set the src attribute of the picture, otherwise the default picture will be displayed
There are two ways to achieve lazy loading of images. One is to bind srcoll events for monitoring, and the other is to use IntersectionObserver to determine whether the image has reached the visual area.
But there are browser compatibility issues.
The following encapsulates two methods of compatible with a lazy loading instruction to determine whether the browser supports the IntersectionObserver API.
If supported, use IntersectionObserver to implement lazy loading, otherwise use the srcoll event listening + throttling method.
When using:<img v-LazyLoad="" /> Change the src of the tag in the component to v-LazyLoad
export default { install(Vue, options) { const defaultSrc = ; ("lazy", { bind(el, binding) { (el, , defaultSrc); }, inserted(el) { if (IntersectionObserver) { (el); } else { (el); } }, }); }, // Initialization init(el, val, def) { ("data-src", val); ("src", def); }, // Use IntersectionObserver to listen on el observe(el) { var io = new IntersectionObserver((entries) => { const realSrc = ; if (entries[0].isIntersecting) { if (realSrc) { = realSrc; ("data-src"); } } }); (el); }, // Listen to scroll events listenerScroll(el) { const handler = (, 300); (el); ("scroll", () => { handler(el); }); }, // Load real pictures load(el) { const windowHeight = ; const elTop = ().top; const elBtm = ().bottom; const realSrc = ; if (elTop - windowHeight < 0 && elBtm > 0) { if (realSrc) { = realSrc; ("data-src"); } } }, // throttling throttle(fn, delay) { let timer; let prevTime; return function (...args) { const currTime = (); const context = this; if (!prevTime) prevTime = currTime; clearTimeout(timer); if (currTime - prevTime > delay) { prevTime = currTime; (context, args); clearTimeout(timer); return; } timer = setTimeout(function () { prevTime = (); timer = null; (context, args); }, delay); }; }, };
Verification of permissions for specified containers on the page
background:
In some background management systems, we may need to make some judgments on operational permissions based on user roles.
Many times we roughly add v-if/v-show to an element to display and hide it.
However, if the judgment conditions are cumbersome and multiple places need to be judged, the code in this method is not only inelegant but also redundant.
In this case, we can handle it through global custom directives.
Requirements: Customize a permission directive to display and hide the Dom that requires permission judgment.
Ideas:
Customize an array of permissions
Determine whether the user's permissions are in this array. If so, it will be displayed. Otherwise, it will be removed.
function checkArray(key) { let arr = ["1", "2", "3", "4"]; let index = (key); if (index > -1) { return true; // Permissions } else { return false; // No permission } } export default { inserted: function (el, binding) { let permission = ; // Get the value of v-permission if (permission) { let hasPermission = checkArray(permission); if (!hasPermission) { // No permissions to remove Dom elements && (el); } } }, };
When using: Example
<!-- show --> <button v-permission="'1'">Permission Button1</button> <!-- 不show --> <button v-permission="'10'">Permission Button2</button></div>
Add watermark to the page
Requirements: Add background watermark to the entire page
Ideas:
Use the canvas feature to generate a base64 format image file, set its font size, color, etc.
Set it as a background image to achieve the page or component watermark effect
function addWaterMarker(str, parentNode, font, textColor) { // Watermark text, parent element, font, text color var can = ("canvas"); (can); = 200; = 150; = "none"; var cans = ("2d"); ((-20 * ) / 180); = font || "16px Microsoft JhengHei"; = textColor || "rgba(180, 180, 180, 0.3)"; = "left"; = "Middle"; (str, / 10, / 2); = "url(" + ("image/png") + ")"; } export default { bind: function (el, binding) { addWaterMarker(, el, , ) }, }
When using:
<div v-waterMarker="{text:'bjg All rights reserved',textColor:'rgba(180, 180, 180, 0.4)'}"></div>
Specify DOM drag and drop
need:
Implement a drag and drop command, which can drag elements in any viewing area of the page.
Ideas:
Set the element that needs to be dragged is relative positioning, and its parent element is absolute positioning.
Record the current left and top values of the target element when the mouse is pressed (onmousedown).
When the mouse is onmousemove, the change values of the horizontal and vertical distances of each movement are calculated, and the left and top values of the element are changed.
Complete drag and drop when the mouse is released (onmouseup)
export default { inserted: function (el) { = "move"; = function (e) { let disx = - ; let disy = - ; = function (e) { let x = - disx; let y = - disy; let maxX = - parseInt((el).width); let maxY = - parseInt((el).height); if (x < 0) { x = 0; } else if (x > maxX) { x = maxX; } if (y < 0) { y = 0; } else if (y > maxY) { y = maxY; } = x + "px"; = y + "px"; }; = function () { = = null; }; }; }, };
When using:
<div class="el-dialog" v-draggable></div>
Specify whether the container is displayed based on the content
If the object or array passed in is empty, the DOM element will not be displayed
// Determine whether the object or array is emptyfunction isEmpty(obj) { if ((obj)) { return === 0; } else if (typeof obj === "object") { return (obj).length === 0; } return true; // Other types are empty by default} export default { inserted: function (el, binding) { const value = ; if (isEmpty(value)) { = "none"; } }, };
The above is the detailed explanation of the packaging of Vue2's commonly used global custom instructions. For more information about Vue2's global custom instructions, please pay attention to my other related articles!