How to customize directive in vue

catalogue

1, What is a custom directive

2, How to customize instructions

3, Application scenario

1, What is a custom directive

The in-line attributes at the beginning of v-are all instructions. Different instructions can complete or implement different functions. When performing bottom-level operations on ordinary DOM elements, custom instructions will be used. In addition to the core functions, the default built-in instructions (V- model And v-show), Vue also allows you to register custom instructions

Several ways of using instructions:

//An instruction will be instantiated, but the instruction has no parameters 
`v-xxx`
 
// --Pass value to instruction
`v-xxx="value"`  
 
// --Pass the string into the instruction, such as ` v-html = "'< p > content < / P >'"`
`v-xxx="'string'"` 
 
// --Pass parameters (` arg '), such as ` v-bind:class="className"`
`v-xxx:arg="value"` 
 
// --Use modifier (` modifier ')
`v-xxx:arg.modifier="value"` 

2, How to customize instructions

Register a user-defined instruction, including global registration and local registration

Global registration is mainly used Vue Register with the direct method

Vue. The first parameter of directive is the name of the instruction (no v-prefix is required), and the second parameter can be object data or an instruction function

// Register a global custom instruction ` v-focus`
Vue.directive('focus', {
  // When the bound element is inserted into the DOM
  inserted: function (el) {
    // Focus element
    el.focus()  // A small function that automatically enables the input box to get the focus after the page is loaded
  }
})

Local registration is achieved by setting the directive property in the component options option

directives: {
  focus: {
    // Definition of instruction
    inserted: function (el) {
      el.focus() // A small function that automatically enables the input box to get the focus after the page is loaded
    }
  }
}

Then you can use the new "v-focus" property on any element in the template, as follows:

<input v-focus />

Hook function

Custom instructions also have hook functions like components:

bind: called only once, when the instruction is bound to the element for the first time. One time initialization settings can be performed here

Inserted: called when the bound element is inserted into the parent node (only the parent node is guaranteed to exist, but it may not have been inserted into the document)

Update: called when the VNode of the component is updated, but it may occur before its child VNode is updated. The value of the instruction may or may not have changed. However, you can ignore unnecessary template updates by comparing the values before and after the update

componentUpdated: the VNode and its sub VNode of the instruction component are updated after all updates.

unbind: called only once when the instruction is unbound from the element

All hook functions have the following parameters:

el: the element bound by the instruction, which can be used to directly operate DOM

binding: an object containing the following properties:
 

`name`: Instruction name, excluding v- Prefix.

`value`: Binding value of the instruction, for example: v-my-directive="1 + 1" In, the binding value is 2.

`oldValue`: The previous value of the instruction binding, only in update and componentUpdated Available in hooks. Available whether the value changes or not.

`expression`: An instruction expression in the form of a string. for example v-my-directive="1 + 1" In, the expression is "1 + 1". 

`arg`: Parameters passed to the instruction, optional. for example v-my-directive:foo In, the parameter is "foo". 

`modifiers`: An object that contains modifiers. For example: v-my-directive.foo.bar In, the modifier object is { foo: true, bar: true }

`vnode`: Vue Compiled virtual node

`oldVnode`: Previous virtual node, only in update and componentUpdated Available in hook

Except el, other parameters should be read-only and should not be modified. If you need to share data between hooks, it is recommended to do so through the dataset of the element
for instance:

<div v-demo="{ color: 'white', text: 'hello!' }"></div>
<script>
    Vue.directive('demo', function (el, binding) {
    console.log(binding.value.color) // "white"
    console.log(binding.value.text)  // "hello!"
    })
</script>

3, Application scenario

Using custom components can meet some of our daily scenarios. Here are some examples of custom components:

  • Anti shake

  • Lazy loading of pictures

  • One click Copy function

Input box anti shake

In this case, a v-throttle custom instruction is set to implement anti shake

for instance:

// 1. Set v-throttle custom instruction
Vue.directive('throttle', {
  bind: (el, binding) => {
    let throttleTime = binding.value; // Anti shake time
    if (!throttleTime) { // If the user does not set the anti shake time, it defaults to 2s
      throttleTime = 2000;
    }
    let cbFun;
    el.addEventListener('click', event => {
      if (!cbFun) { // First execution
        cbFun = setTimeout(() => {
          cbFun = null;
        }, throttleTime);
      } else {
        event && event.stopImmediatePropagation();
      }
    }, true);
  },
});
// 2. Set the v-throttle custom instruction for the button label
<button @click="sayHello" v-throttle>Submit</button>

Lazy loading of pictures

Set up a v-lazy custom component to complete the lazy loading of pictures

const LazyLoad = {
    // install method
    install(Vue,options){
       // loading diagram instead of picture
        let defaultSrc = options.default;
        Vue.directive('lazy',{
            bind(el,binding){
                LazyLoad.init(el,binding.value,defaultSrc);
            },
            inserted(el){
                // Compatible processing
                if('InterpObserver' in window){
                    LazyLoad.observe(el);
                }else{
                    LazyLoad.listenerScroll(el);
                }
                
            },
        })
    },
    // initialization
    init(el,val,def){
        // src stores the real src
        el.setAttribute('src',val);
        // Set src as loading graph
        el.setAttribute('src',def);
    },
    // Using InterpObserver to monitor el
    observe(el){
        let io = new InterpObserver(entries => {
            let realSrc = el.dataset.src;
            if(entries[0].isIntersecting){
                if(realSrc){
                    el.src = realSrc;
                    el.removeAttribute('src');
                }
            }
        });
        io.observe(el);
    },
    // Listen for scroll events
    listenerScroll(el){
        let handler = LazyLoad.throttle(LazyLoad.load,300);
        LazyLoad.load(el);
        window.addEventListener('scroll',() => {
            handler(el);
        });
    },
    // Load real pictures
    load(el){
        let windowHeight = document.documentElement.clientHeight
        let elTop = el.getBoundingClientRect().top;
        let elBtm = el.getBoundingClientRect().bottom;
        let realSrc = el.dataset.src;
        if(elTop - windowHeight<0&&elBtm > 0){
            if(realSrc){
                el.src = realSrc;
                el.removeAttribute('src');
            }
        }
    },
    // throttle
    throttle(fn,delay){
        let timer; 
        let prevTime;
        return function(...args){
            let currTime = Date.now();
            let 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;

One click Copy function

import { Message } from 'ant-design-vue';
 
const vCopy = { //
  /*
    bind Hook function, called when Binding for the first time, can be initialized here
    el: Acting dom object
    value: The value passed to the instruction, that is, the value we want to copy
  */
  bind(el, { value }) {
    el.$value = value; // Use a global attribute to store the value passed in, because this value will be used in other hook functions
    el.handler = () => {
      if (!el.$value) {
      // When the value is empty, the prompt will be given. The prompt here is ant design Vue, and you are free to use it
        Message.warning('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 recalled under iOS, and move the textarea out of the visual area at the same time
      textarea.readOnly = 'readonly';
      textarea.style.position = 'absolute';
      textarea.style.left = '-9999px';
      // Assign the value to copy to the value attribute of the textarea tag
      textarea.value = el.$value;
      // Insert textarea into the body
      document.body.appendChild(textarea);
      // Select the value and copy it
      textarea.select();
      // textarea.setSelectionRange(0, textarea.value.length);
      const result = document.execCommand('Copy');
      if (result) {
        Message.success('Copy successful');
      }
      document.body.removeChild(textarea);
    };
    // Binding click events 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 an instruction is unbound from an element, the event binding is removed
  unbind(el) {
    el.removeEventListener('click', el.handler);
  },
};
 
export default vCopy;

Drag

<div ref="a" id="bg" v-drag></div>

  directives: {
    drag: {
      bind() {},
      inserted(el) {
        el.onmousedown = (e) => {
          let x = e.clientX - el.offsetLeft;
          let y = e.clientY - el.offsetTop;
          document.onmousemove = (e) => {
            let xx = e.clientX - x + "px";
            let yy = e.clientY - y + "px";
            el.style.left = xx;
            el.style.top = yy;
          };
          el.onmouseup = (e) => {
            document.onmousemove = null;
          };
        };
      },
    },
  },

Keywords: Javascript Front-end Vue.js

Added by bbreslauer on Sun, 13 Feb 2022 12:53:50 +0200